/*+++++++++++++++++
  refdbc - the refdb client console application
  markus@mhoenicka.de 2-10-00
  $Id: refdbc.c,v 1.72.2.16 2006/02/08 20:37:20 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: several fns use an ugly hack to use cmdln_tokenize. Tidy up */

/* temporary hack to include cgi features. This should be selectable via
   a configure switch */
#define REFDB_CGI 1

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

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <readline/readline.h>
#include <readline/history.h>

#include <unistd.h> 
#include <limits.h>
#include <signal.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 for memory handling */
#include "pref.h" /* for init file, depends on linklist.h */
#include "strfncs.h" /* for is functions */
#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 functions */
#include "refdbc.h" /* stuff specific to this client */
#include "readris.h" /* fncs to access RIS files */
#include "connect.h" /* modified read/write for sockets */
#include "tokenize.h" /* cuts command line into tokens */
#include "cgi.h" /* cgi related stuff */
#include "passwd.h" /* securely obtain passwords */

extern const char cs_term[];

/*+ 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 refdbc" },
  { "addlink", com_addlink, "Link notes to references, authors etc." },
  { "addnote", com_addnote, "Add notes to the database" },
  { "addref", com_addref, "Add references to the database" },
  { "checkref", com_checkref, "Check references" },
  { "countnote", com_countnote, "Counts matching notes" },
  { "countref", com_countref, "Counts matching references" },
  { "deletelink", com_deletelink, "Unlink notes to references, authors etc." },
  { "deletenote", com_deletenote, "Delete notes from the database" },
  { "deleteref", com_deleteref, "Delete references from the database" },
  { "dumpref", com_dumpref, "Remove references from your personal interest list"},
  { "getas", com_getas, "Get a list of series editors" },
  { "getau", com_getau, "Get a list of part authors" },
  { "getax", com_getax, "Get a list of any authors/editors" },
  { "geted", com_geted, "Get a list of publication authors/editors" },
  { "getkw", com_getkw, "Get a list of keywords" },
  { "getjf", com_getjf, "Get a list of journal names (full)" },
  { "getjo", com_getjo, "Get a list of journal names (abbrev)" },
  { "getj1", com_getj1, "Get a list of journal names (custom abbrev1)" },
  { "getj2", com_getj2, "Get a list of journal names (custom abbrev2)" },
  { "getref", com_getref, "Get a list of references" },
  { "getnote", com_getnote, "Get a list of notes" },
  { "listdb", com_listdb, "List databases" },
  { "liststyle", com_liststyle, "List bibliography styles" },
  { "pickref", com_pickref, "Add references to your personal interest list"},
  { "selectdb", com_selectdb, "Select a database" },
  { "set", com_setvalue, "Set new value of config variable" },
  { "updatenote", com_updatenote, "Update notes in the database" },
  { "updateref", com_updateref, "Update references in the database" },
  { "verbose", com_verbose, "Toggle verbose mode" },
  { "updatejo", com_updatejo, "Update journal name synonyms" },
  { "whichdb", com_whichdb, "Show info about current database" },
  { (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[24] = {
  {"serverip", ""},
  {"port", ""},
  {"verbose", ""},
  {"pager", ""},
  {"username", ""},
  {"passwd", ""},
  {"defaultdb", ""},
  {"pdfroot", ""},
  {"timeout", ""},
  {"logfile", ""},
  {"logdest", ""},
  {"loglevel", ""},
  {"autokill", ""},
  {"fields", ""},
  {"cssurl", ""},
  {"defaultris", ""},
  {"refdblib", ""},
  {"reftype", ""},
  {"toencoding", ""},
  {"fromencoding", ""},
  {"outtype", ""},
  {"check_fields", ""},
  {"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 pdfroot[PREFS_BUF_LEN] = ""; /* root path for pdf or other offprint files */
char verbose[PREFS_BUF_LEN] = "f"; /*+ 1 = verbose output, 0 = terse output +*/
char log_file[PREFS_BUF_LEN] = "/var/log/refdbc.log"; /*+ default log file +*/
char log_dest[PREFS_BUF_LEN] = "1"; /*+ 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 autokill[PREFS_BUF_LEN] = "1800"; /*+ default time in seconds until the CGI app will kill itself +*/
char default_fields[PREFS_BUF_LEN] = ""; /*+ specifies additional fields which should always be displayed +*/
char check_fields[PREFS_BUF_LEN] = ""; /*+ fields which should be checked for duplicates +*/
char css_url[PREFS_BUF_LEN] = ""; /*+ specifies URL of a css stylesheet for the getref html output +*/
char default_ris[PREFS_BUF_LEN] = ""; /*+ default ris fields used in addref +*/
char refdblib[PREFS_BUF_LEN] = ""; /*+ path to shareable files +*/
char current_db[PREFS_BUF_LEN] = "";
char fromencoding[PREFS_BUF_LEN] = ""; /*+ encoding of input +*/
char toencoding[PREFS_BUF_LEN] = ""; /*+ encoding for output +*/
char reftype[PREFS_BUF_LEN] = "ris"; /* input data type (ris|risx) */
char outtype[PREFS_BUF_LEN] = ""; /* output data type (screen|html) */
char confdir[_POSIX_PATH_MAX] = ""; /* 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[] = "refdbc";
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_batchmode = 0; /* 1 if we're running in batch mode */
int n_read_stdin = 0; /* if 1, data try to squeeze in at stdin */
int n_cgi = 0; /* if 1, we run as a cgi app */
unsigned int n_autokill = 1800; /* numeric version of autokill */

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

/* prototypes of local functions */
static int addref(char *arg, int update_ref);
static int addnote(char *arg, int update_note);
static int addlink (int n_remove, char* arg);
static int getfoo(char *arg, int type);
static int getref (char* arg, int send_data);
static int getnote (char* arg, int send_data);
static int pickref (char* arg, int n_remove);
/* static int valid_argument (char *caller, 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, 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 *line, *s;
  char the_opt[5];
  char the_cmd[32] = "";
  char delayed_cgi_errmsg[64] = "";
  char* request_method;
  char* content_length;
  char* cgi_data;
  char* orig_cgi_data;
  char *the_command;
  char *new_the_command;
  size_t the_command_len = 512;
  size_t n_content_length;
  int n_opt, i, j;
  int n_readinit = 1; /* if 1, read config file. If 0, skip config file */
  int retval = 0;
  FILE* errstream;
#ifdef REFDB_CGI
  struct liliform sentinel;
  struct liliform* ptr_current;
#endif

  struct sigaction act, oldact, intact, oldintact, alrm, oldalrm;

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

  /* look for batch command. If found, don't test for CGI environment
     and change -h behaviour */
  for (i = 0; i < argc; i++) {
    if (argv[i][0] == '-' && argv[i][1] == 'C') {
      n_batchmode = 1;
      break;
    }
  }

#ifdef REFDB_CGI
  if (!n_batchmode) {
    n_cgi = is_cgi(&request_method, &content_length, delayed_cgi_errmsg); /* 1 if we are running as a cgi app */
  }
#endif

  /* redirect error messages correctly */
  if (n_cgi) {
    errstream = stdout;
  }
  else {
    errstream = stderr;
  }

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

  /* 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) {
    write_err("initializing signal handlers failed\n");
    LOG_PRINT(LOG_ERR, "could not initialize signal handlers");
    exit(1);
  }

#ifdef REFDB_CGI
  if (n_cgi) {
    alrm.sa_handler = alrmhandler;
    sigemptyset(&alrm.sa_mask);
    alrm.sa_flags = 0;

    if (sigaction(SIGALRM, &alrm, &oldalrm) != 0) {
      write_err("initializing signal handler failed\n");
      LOG_PRINT(LOG_ERR, "could not initialize signal handler");
      exit(1);
    }
  }
#endif

  /* 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 = current_db;
  prefs[7].varvalue = pdfroot;
  prefs[8].varvalue = refdb_timeout;
  prefs[9].varvalue = log_file;
  prefs[10].varvalue = log_dest;
  prefs[11].varvalue = log_level;
  prefs[12].varvalue = autokill;
  prefs[13].varvalue = default_fields;
  prefs[14].varvalue = css_url;
  prefs[15].varvalue = default_ris;
  prefs[16].varvalue = refdblib;
  prefs[17].varvalue = reftype;
  prefs[18].varvalue = toencoding;
  prefs[19].varvalue = fromencoding;
  prefs[20].varvalue = outtype;
  prefs[21].varvalue = check_fields;
  prefs[22].varvalue = no_encrypt;

  /* look for custom config directory */
  for (i = 0; i < argc; i++) {
    if (argv[i][0] == '-' && argv[i][1] == 'y') {
      strncpy(confdir, argv[i+1], _POSIX_PATH_MAX);
      confdir[_POSIX_PATH_MAX] = '\0';
      break;
    }
  }

  if (!n_cgi) {
    /* a slimy hack to detect the -q option before we run getopt */
    for (i = 0; i < argc; i++) {
      if (argv[i][0] == '-' && argv[i][1] == 'q') {
	n_readinit = 0;
	break;
      }
    }

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

    /* read command line settings. These may override the config file settings */
    while ((n_opt = getopt(argc, argv, "aA:b:c:C:d:e:E:f:F:g:G:hi:l:L:kn:N:o:O:p:PqQr:R:s:S:t:T:u:U:vVw: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';

	/* see whether a valid command was requested */
	if (!find_command(optarg, commands)) {
	  fprintf (stderr, "%s: No such command for refdb.\n", optarg);
	  exit (1);
	}
/* 	n_batchmode was set at startup */
	break;
      case 'd':
	strncpy(current_db, optarg, PREFS_BUF_LEN);
	current_db[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'e':
	strncpy(log_dest, optarg, PREFS_BUF_LEN);
	log_dest[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'F':
	strncpy(default_fields, optarg, PREFS_BUF_LEN);
	default_fields[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'g':
	strncpy(default_ris, optarg, PREFS_BUF_LEN);
	default_ris[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'G':
	strncpy(css_url, optarg, PREFS_BUF_LEN);
	css_url[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'h':
	/* if we're running in batch mode, assume the -h option pertains
	   to the requested command. Otherwise display the generic program
	   help message */
	if (!n_batchmode) {
	  fprintf(stderr, "Usage: refdbc [-c pager] [-C command] [-d db] [-e logdest] [-F fields] [-G url] [-h] [-i address] [-l loglevel] [-L logfile] [-p port] [-q] [-R pdfroot] [-T time] [-u name] [-v] [-V] [-w password] [-x] [-y confdir]\nOptions: -c command line of pager\n         -C run command in batch mode\n         -d use database db\n         -e log destination (0=stderr;1=syslog;2=custom file)\n         -F additional fields in reference output\n         -G URL of a css stylesheet for HTML output\n         -h prints this help\n         -i set server IP address\n         -l set log level (0<=level<=7)\n         -L full path of custom log file\n         -p set server port\n         -q ignore init-file\n         -R set pdf root directory\n         -T set timeout in seconds\n         -u use this username\n         -v show version information\n         -V switch to verbose mode\n         -w set password (use '*' to be asked interactively)\n         -x do not encrypt passwords\n         -y look for configuration files in confdir\n");
	  exit (0);
	}
	else {
	  /* pass through the -h option to the command argument list */
	  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 'R':
	strncpy(pdfroot, optarg, PREFS_BUF_LEN);
	pdfroot[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'T':
	strncpy(refdb_timeout, optarg, PREFS_BUF_LEN);
	refdb_timeout[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'u':
	strncpy(username, optarg, PREFS_BUF_LEN);
	username[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'v':
	fprintf(stderr, "refdbc %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';
	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 will take care of */
      case 'a': /* fall through, we assemble the command string */
      case 'A':
      case 'b':
      case 'E':
      case 'f':
      case 'k':
      case 'n':
      case 'N':
      case 'o':
      case 'O':
      case 'P':
      case 'Q':
      case 'r':
      case 's':
      case 'S':
      case 't':
      case 'U':
	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: refdbc [-c pager] [-C command] [-d db] [-e logdest] [-F fields] [-G url] [-h] [-i address] [-l loglevel] [-L logfile] [-p port] [-q] [-R pdfroot] [-T time] [-u name] [-v] [-V] [-w password] [-x] [-y confdir]\nOptions: -c command line of pager\n         -C run command in batch mode\n         -d use database db\n         -e log destination (0=stderr;1=syslog;2=custom file)\n         -F additional fields in reference output\n         -G URL of a css stylesheet for HTML output\n         -h prints this help\n         -i set server IP address\n         -l set log level (0<=level<=7)\n         -L full path of custom log file\n         -p set server port\n         -q ignore init-file\n         -R set pdf root directory\n         -T set timeout in seconds\n         -u use this username\n         -v show version information\n         -V switch to verbose mode\n         -w set 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 refdbc -h to display usage\n", optopt);
	exit (1);
	break;
      }
    }
  }
  else { /* if we run as cgi */
    read_prefs(prefs, "refdbcgirc", 0);
  }

  /* 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("loglevel");
  postprocess_var("logdest");
  n_oldlog_dest = n_log_dest; /* keep a backup copy if changed by set */
  postprocess_var("timeout");
  postprocess_var("verbose");
  postprocess_var("defaultris");

#ifdef REFDB_CGI
  if (n_cgi) { /* no killer alarm in interactive mode */
    n_autokill = (unsigned int)atoi(autokill);
  
    /* this alarm will go off after n_autokill seconds and cause the CGI app
       to exit as it is apparently hanging */
    if (n_autokill) {
      alarm(n_autokill);
    }
  }
#endif

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

  /* now report incorrect cgi input, if any */
  if (delayed_cgi_errmsg[0] && n_cgi) {
    LOG_PRINT(LOG_ERR, delayed_cgi_errmsg);
    exit(1);
  }

  /* 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 (!n_cgi && postprocess_var("username")) {
    fprintf(stderr, "incorrect username\n");
    LOG_PRINT(LOG_CRIT, "incorrect username");
    exit (1);
  }

  /* see whether we need a password */
  if (!n_cgi) {
    postprocess_var("passwd");
  }

  if (n_cgi) { /* we're running as a cgi app */
#ifdef REFDB_CGI
    /* make sure the output goes to where it is supposed to */
    strcpy(the_pager, "stdout");

    /* initialize linked list */
    sentinel.name[0] = '\0';
    sentinel.value = NULL;
    sentinel.ptr_next = NULL;

    /* read string from stdin and chop it */
    n_content_length = atoi(content_length);

    cgi_data = malloc(n_content_length + 1);
    if (!cgi_data) {
      cgi_header(CGI_PLAIN);
      fprintf(errstream, "out of memory\n");
      LOG_PRINT(LOG_WARNING, "out of memory");
      exit(1);
    }

    orig_cgi_data = malloc(n_content_length + 1);
    if (!orig_cgi_data) {
      cgi_header(CGI_PLAIN);
      fprintf(errstream, "out of memory\n");
      LOG_PRINT(LOG_WARNING, "out of memory");
      free(cgi_data);
      exit(1);
    }

    if (fread(cgi_data, 1, n_content_length, stdin) != n_content_length) {
      cgi_header(CGI_PLAIN);
      fprintf(errstream, "could not read cgi data\n");
      free(cgi_data);
      free(orig_cgi_data);
      LOG_PRINT(LOG_ERR, "could not read cgi data");
      exit(1);
    }

    cgi_data[n_content_length] = '\0'; /* convert to a C-style string */
    LOG_PRINT(LOG_DEBUG, cgi_data);

    /* keep copy of original string as decode_cgi() will modify the latter */
    strcpy(orig_cgi_data, cgi_data);

    if (decode_cgi(cgi_data, &sentinel)) {
      delete_all_liliform(&sentinel);
      free(cgi_data);
      cgi_header(CGI_PLAIN);
      fprintf(errstream, "could not decode cgi data\n");
      LOG_PRINT(LOG_ERR, "could not decode cgi data");
      exit(1);
    }

    free(cgi_data);

    /* look for "addformat" and decide if a converter should be
       run. If so, use popen and write original CGI string to the 
       file descriptor, then exit */
    ptr_current = get_liliform(&sentinel, "addformat");
    if (ptr_current) {
      if (strcmp(ptr_current->value, "bib2ris") == 0
	  || strcmp(ptr_current->value, "nmed2ris") == 0
	  || strcmp(ptr_current->value, "med2ris.pl") == 0) {
	FILE* ptr_file;
	size_t n_byte_written;
	char convert_cmd[_POSIX_PATH_MAX+1] = "";

	/* running the external converter requires the full path of
	   the executable. We use the current working directory and
	   assume that the converter binaries are in the same directory,
	   i.e. the /cgi/bin/ directory, which is a promising assumption */
	if (getcwd(convert_cmd, _POSIX_PATH_MAX+1) == NULL) {
	  cgi_header(CGI_PLAIN);
	  fprintf(errstream, "could not obtain path\n");
	  LOG_PRINT(LOG_ERR, "could not obtain path");
	  free(orig_cgi_data);
	  delete_all_liliform(&sentinel);
	  exit(1);
	}
	strncat(convert_cmd, "/", _POSIX_PATH_MAX-strlen(convert_cmd));
	strncat(convert_cmd, ptr_current->value, _POSIX_PATH_MAX-strlen(convert_cmd));

	/* run external app in write mode */
	ptr_file = popen(convert_cmd, "w");
	if (ptr_file) {
	  LOG_PRINT(LOG_DEBUG, "write data to converter");
	  if ((n_byte_written = fwrite((const void*)orig_cgi_data, sizeof(char), n_content_length, ptr_file)) < n_content_length) {
	    cgi_header(CGI_PLAIN);
	    fprintf(errstream, "could not write to converter\n");
	    LOG_PRINT(LOG_ERR, "could not write to converter");
	    free(orig_cgi_data);
	    pclose(ptr_file);
	    delete_all_liliform(&sentinel);
	    exit(1);
	  }
	  pclose(ptr_file);
	  free(orig_cgi_data);
	  delete_all_liliform(&sentinel);
	  exit(1);
	}
	else {
	  cgi_header(CGI_PLAIN);
	  fprintf(errstream, "could not start %s\n", ptr_current->value);
	  LOG_PRINT(LOG_ERR, "could not start converter");
	  delete_all_liliform(&sentinel);
	  free(orig_cgi_data);
	  exit(1);
	}
      }
      /* else: refuse to run anything else but our known converters */
    }

    /* fill globals */
    ptr_current = get_liliform(&sentinel, "name");
    if (ptr_current) {
      strcpy(username, ptr_current->value);
    }
    ptr_current = get_liliform(&sentinel, "passwd");
    if (ptr_current) {
      strcpy(passwd, ptr_current->value);
    }
    ptr_current = get_liliform(&sentinel, "database");
    if (ptr_current) {
      strcpy(current_db, ptr_current->value);
    }

/*  #ifdef dontwantthis */
    /* deduce the command that should be run */
    /* the get_liliform calls will return only a ptr if the name matches
       and the value is not NULL, i.e. only if the variable has a value */
    if (get_liliform(&sentinel, "simpquery1") || get_liliform(&sentinel, "advquery")) {
      retval = com_getref((char*)&sentinel); /* this nasty typecast will be reversed by
				       the called fn */
    }
    else if (get_nliliform(&sentinel, "editid", 6)) {
      retval = com_getref((char*)&sentinel); 
    }
    else if (get_liliform(&sentinel, "addref")) {
      retval = com_addref((char*)&sentinel); 
    }
    else if (get_liliform(&sentinel, "deleteref")) {
      retval = com_deleteref((char*)&sentinel); 
    }
    else if (get_liliform(&sentinel, "updateref")) {
      retval = com_updateref((char*)&sentinel); 
    }
    else if (get_liliform(&sentinel, "selectdb")) {
      retval = com_selectdb((char*)&sentinel); 
    }
    else if (get_liliform(&sentinel, "pickref") || get_liliform(&sentinel, "unpickref")) {
      retval = com_pickref((char*)&sentinel); 
    }
    else if (get_liliform(&sentinel, "dbregexp")) {
      retval = com_listdb((char*)&sentinel); 
    }
    else if (get_liliform(&sentinel, "kajquery")) {
      retval = getfoo((char*)&sentinel, -1);
    }
    else {
      delete_all_liliform(&sentinel);
      cgi_header(CGI_PLAIN);
      fprintf(errstream, "unknown command\n");
      LOG_PRINT(LOG_ERR, "unknown command");
      exit(1);
    }
/*  #endif */
    /* test */

    /* display all name/value pairs */
/*      cgi_header(CGI_PLAIN); */
/*      ptr_current = sentinel.ptr_next; */

/*      while (ptr_current) { */
/*        printf(ptr_current->name); */
/*        printf("\n"); */
/*        printf(ptr_current->value); */
/*        printf("\n"); */
/*        ptr_current = ptr_current->ptr_next; */
/*      } */
/*      exit(0); */

    /* end test */

    /* clean up */
    delete_all_liliform(&sentinel);
#endif
  }
  else 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, "refdbc %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);
    }

    if (*current_db) {
      com_selectdb(current_db);
/*       fprintf(stderr, "I will try to use\n%s\nas the current database.\n", current_db); */
      LOG_PRINT(LOG_INFO, current_db);
    }

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

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

      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 */
	n_broken_pipe = 0;   /*	to interrupt a stalled connection, but
				rather randomly */
      }

      free (line);
    }
  }
  exit (retval);
}

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

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

/* **************************************************************** */
/*                                                                  */
/*                   refdbc Commands                                */
/*                                                                  */
/* **************************************************************** */


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_addref(): adds references to a database

  int com_addref  0 if successful, 1 on error

  char *arg the name of the file containing the references

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_addref (char* arg)
{
  return addref(arg, 0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_updateref(): updates references in a database

  int com_updateref  0 if successful, 1 on error

  char *arg the name of the file containing the references

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_updateref (char* arg)
{
  return addref(arg, 1);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_checkref(): check references

  int com_checkref  0 if successful, 1 on error

  char *arg the name of the file containing the references

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_checkref (char* arg)
{
  return addref(arg, 2);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_deleteref(): delete a reference

  int com_deleteref 0 if successful, 1 if error 

  char *arg the spec of the reference which is to be deleted

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_deleteref (char* arg)
{
  char *new_outbuffer = NULL;
  char cmd_buffer[OUTBUF_LEN] = "";
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char **inargv = NULL; /* tokens of the argument */
  char *newarg = NULL;
  char* infile = NULL;
  char db[DBNAME_LENGTH] = "";
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  int numbyte = 0;
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int n_cmdlinerror = 0;
  int result;
  int n_opt;
  int i;
  int n_read_file = 0;
  int cs_status;
  size_t outbuf_len;
  FILE *infilefp;
  FILE* errstream;
  struct simplelistvals slvals;
  struct lilimem sentinel;

  errstream = (n_cgi) ? stdout : stderr;

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

  /* get us some buffer for output */
  outbuf_len = 128; /* something to start with */
  slvals.outbuffer = malloc(outbuf_len); 
  if (slvals.outbuffer == NULL) {
    return 1;
  }
  slvals.outbuffer[0] = '\0';

  slvals.n_file_open = 0;
  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = NULL;
  slvals.outpipe = NULL;

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

  strcpy(db, current_db); /* use default db if set */

  strcpy(cmd_buffer, "deleteref ");

  /* 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) {
    delete_all_lilimem(&sentinel);
    return 1;
  }
  
  if (insert_lilimem(&sentinel, (void**)&inargv, NULL)) {
    delete_all_lilimem(&sentinel);
    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, "deleteref ");
  strcat(newarg, arg);
  
  result = cmdln_tokenize(&inargc, &inargv, inargcmax, newarg);


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

  /* get options */
  optind = 0;

  while ((n_opt = getopt(inargc, inargv, "aA:c:C:d:e:E:f:F:g:G:hi:kl:L:o:O:p:Pqr:R:s:S:t:T:u: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 'd':
      /*        printf("-d %s\n", optarg); */
      strcpy(db, optarg); /* override preset db */
      break;
    case 'f':
      /*        printf("-f %s\n", optarg); */
      if (!strcmp(optarg, "stdin")) {
	n_read_stdin = 1;
      }
      else {
	infile = canonicalize_path(optarg);
	if (insert_lilimem(&sentinel, (void**)&infile, NULL)) {
	  delete_all_lilimem(&sentinel);
	  return 1;
	}
	n_read_file = 1;
      }
      break;
    case 'h':
      printf("Deletes the specified references from the database\nSyntax: deleteref [-c command] [-d database] [-h] [-o outfile] [-O outfile] {ID|-f infile}\nOptions: -c command   pipe the output through command\n         -d database  specify the database to work with\n         -f infile    Read the reference IDs from file infile\n         -h           prints this mini-help\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         All other arguments are interpreted as IDs to delete.\n");
      delete_all_lilimem(&sentinel);
      return 0;
      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;
      /* now all the options that main has already taken care of */
    case 'a':
    case 'A':
    case 'C': /* fall through - nothing to do */
    case 'e':
    case 'E':
    case 'F':
    case 'g':
    case 'G':
    case 'i':
    case 'k':
    case 'l':
    case 'L':
    case 'p':
    case 'P':
    case 'q':
    case 'r':
    case 'R':
    case 's':
    case 'S':
    case 't':
    case 'T':
    case 'u':
    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 (!*db) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "Don't know which database to use. Select one with selectdb or use the -d switch with deleteref.\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (n_cmdlinerror) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  /* try to connect to server */
  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 (!n_cgi) {
    /* prepare data */
    if (n_read_file || n_read_stdin || optind == inargc) {
      if (n_read_file) {
	if ((infilefp = fopen(infile, "rb")) == NULL) {
	  delete_all_lilimem(&sentinel);
	  close(slvals.n_sockfd);
	  return 1;
	}
      }
      else {
	infilefp = stdin;
	n_read_stdin = 1; /* not set if optind == inargc */
      }

      result = add_id_from_ris(infilefp, &(slvals.outbuffer), &outbuf_len);

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

      if (result) {
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	return 1;
      }
    }

    for (i = optind; i < inargc; i++) {
      if ((new_outbuffer = mstrcat(slvals.outbuffer, inargv[i], &outbuf_len, 0)) == NULL) {
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	return 1;
      }
      else {
	slvals.outbuffer = new_outbuffer;
      }
      if ((new_outbuffer = mstrcat(slvals.outbuffer, " ", &outbuf_len, 0)) == NULL) {
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	return 1;
      }
      else {
	slvals.outbuffer = new_outbuffer;
      }
    }
  }

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

  if (n_cgi) {
    strcat(cmd_buffer, " -t cgi"); /* request cgi output */
  }

  sprintf(cmd_buffer+strlen(cmd_buffer), " %d", strlen(slvals.outbuffer)+TERM_LEN); 

/*   printf("outbuffer went to: %s<<\n", slvals.outbuffer); */

  /* send command to application server */
  send_status(slvals.n_sockfd, 0, TERM_NO);
  numbyte = tiwrite(slvals.n_sockfd, cmd_buffer, TERM_YES);
  LOG_PRINT(LOG_DEBUG, cmd_buffer);
  if (numbyte == -1) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "could not write to refdbd. Stop\n");
    close(slvals.n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }

  numbyte = 0;

  if ((cs_status = read_status(slvals.n_sockfd)) != 0) {
    fprintf(errstream, "%s\n", get_status_msg(cs_status));
    close(slvals.n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }

  getsimplelist(&slvals, 1);

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

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_pickref(): add references to personal interest
                 list

  int com_pickref 0 if successful, 1 if error 

  char *arg the spec of the reference which is to be deleted

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_pickref (char* arg)
{
  return pickref(arg, 0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_dumpref(): remove references from personal interest
                 list

  int com_pickref 0 if successful, 1 if error 

  char *arg the spec of the reference which is to be deleted

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_dumpref (char* arg)
{
  return pickref(arg, 1);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  pickref(): add or remove references to/from personal interest
                 list

  static int pickref 0 if successful, 1 if error 

  char *arg the spec of the reference which is to be deleted

  int n_remove if 0, picks, if 1, dumps

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int pickref (char* arg, int n_remove)
{
  char *new_outbuffer = NULL;
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char cmd_buffer[OUTBUF_LEN] = "";
  char listname[PREFS_BUF_LEN] = "";
  char **inargv = NULL; /* tokens of the argument */
  char *newarg = NULL;
  char* infile = NULL;
  char db[DBNAME_LENGTH] = "";
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  int numbyte = 0;
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int n_cmdlinerror = 0;
  int result;
  int n_opt;
  int i;
  int n_read_file = 0;
  int cs_status;
  size_t outbuf_len;
  FILE *infilefp;
  FILE* errstream;
  struct simplelistvals slvals;
  struct liliform* ptr_current;
  struct lilimem sentinel;

  errstream = (n_cgi) ? stdout : stderr;

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

  outbuf_len = 128; /* something to start with */
  slvals.outbuffer = malloc(outbuf_len); 
  if (slvals.outbuffer == NULL) {
    return 1;
  }
  slvals.outbuffer[0] = '\0'; /* terminate string */

  slvals.n_file_open = 0;
  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = NULL;
  slvals.outpipe = NULL;

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

  strcpy(cmd_buffer, "pickref ");

  strcpy(db, current_db); /* use default db if set */

  if (n_cgi) {
    /* append the ID values of all checked references to outbuffer */
    if (get_liliform((struct liliform*)arg, "unpickref") != NULL) {
      n_remove = 1;
    }

    ptr_current = (struct liliform*)arg;
    
    while ((ptr_current = get_nliliform(ptr_current, "pickid", 6)) != NULL) {
      if ((new_outbuffer = mstrcat(slvals.outbuffer, &((ptr_current->name)[6]), &outbuf_len, 0)) == NULL) {
	delete_all_lilimem(&sentinel);
	return 1;
      }
      else {
	slvals.outbuffer = new_outbuffer;
      }
      if ((new_outbuffer = mstrcat(slvals.outbuffer, " ", &outbuf_len, 0)) == NULL) {
	delete_all_lilimem(&sentinel);
	return 1;
      }
      else {
	slvals.outbuffer = new_outbuffer;
      }
/*        ptr_current = ptr_current->ptr_next; */
    }
  }
  else { /* not cgi */
/*     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) {
	delete_all_lilimem(&sentinel);
	return 1;
      }

      if (insert_lilimem(&sentinel, (void**)&inargv, NULL)) {
	delete_all_lilimem(&sentinel);
	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, NULL)) {
	delete_all_lilimem(&sentinel);
	return 1;
      }

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

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

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

    /* get options */
    optind = 0;

    while ((n_opt = getopt(inargc, inargv, "aA:b:c:C:d:e:E:f:F:g:G:hi:kl:L:o:O:p:Pqr:R:s:S:t:T:u:U:vVw:")) != -1) {
      switch(n_opt) {
      case 'b':
	strncpy(listname, optarg, PREFS_BUF_LEN);
	listname[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'c':
	/*        printf("-P %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 'd':
	/*        printf("-d %s\n", optarg); */
	strcpy(db, optarg); /* override preset db */
	break;
      case 'f':
	/*        printf("-f %s\n", optarg); */
	if (!strcmp(optarg, "stdin")) {
	  n_read_stdin = 1;
	}
	else {
	  infile = canonicalize_path(optarg);
	  if (insert_lilimem(&sentinel, (void**)&infile, NULL)) {
	    delete_all_lilimem(&sentinel);
	    return 1;
	  }
	  n_read_file = 1;
	}
	break;
      case 'h':
	if (n_remove) {
	  fprintf(errstream, "Removes the specified references from your personal interest list\nSyntax: dumpref [-b listname] [-c command] [-d database] [-h] {ID|-f infile}\nOptions: -b listname  specify a personal list\n         -c command   pipe the output through command\n         -d database  specify the database to work with\n         -f infile    Read the reference IDs from file infile\n         -h           prints this mini-help\n         -o outfile   save the output in the file outfile (overwrite)\n         -O outfile   append the output to the file outfile\n         All other arguments are interpreted as IDs to pick or to remove.\n");
	}
	else {
	  fprintf(errstream, "Adds the specified references from your personal interest list\nSyntax: pickref [-b listname] [-c command] [-d database] [-h] {ID|-f infile}\nOptions: -b listname  specify a personal list\n         -c command   pipe the output through command\n         -d database  specify the database to work with\n         -f infile    Read the reference IDs from file infile\n         -h           prints this mini-help\n         -o outfile   save the output in the file outfile (overwrite)\n         -O outfile   append the output to the file outfile\n         All other arguments are interpreted as IDs to pick or to remove.\n");
	}
	delete_all_lilimem(&sentinel);
	return 0;
	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;
	/* now all the options that main has already taken care of */
      case 'a':
      case 'A':
      case 'C': /* fall through - nothing to do */
      case 'e':
      case 'E':
      case 'F':
      case 'g':
      case 'G':
      case 'i':
      case 'k':
      case 'l':
      case 'L':
      case 'p':
      case 'P':
      case 'q':
      case 'R':
      case 's':
      case 'S':
      case 't':
      case 'T':
      case 'u':
      case 'U':
      case 'v':
      case 'V':
      case 'w':
	break;
      case ':':
	fprintf(errstream, "missing option\n");
	n_cmdlinerror = 1;
	break;
      case '?':
	fprintf(errstream, "unknown option\n");
	n_cmdlinerror = 1;
	break;
      }
    }
  }

  /* get arguments */
/*    for (i = optind; i < inargc; i++) { */
/*      printf("argument %s\n", inargv[i]); */
/*    } */

  if (!*db) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "Don't know which database to use. Select one with selectdb or use the -d switch with pickref.\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (n_cmdlinerror) {
    delete_all_lilimem(&sentinel);
    return 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 (!n_cgi) {
  /* prepare data */
    if (n_read_file || n_read_stdin || optind == inargc) {
      if (n_read_file) {
	if ((infilefp = fopen(infile, "rb")) == NULL) {
	  send_status(slvals.n_sockfd, 112, TERM_NO);
	  delete_all_lilimem(&sentinel);
	  close(slvals.n_sockfd);
	  return 1;
	}
      }
      else {
	infilefp = stdin;
	n_read_stdin = 1; /* not set if optind == inargc */
      }

      result = add_id_from_ris(infilefp, &(slvals.outbuffer), &outbuf_len);

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

      if (result) {
	send_status(slvals.n_sockfd, 112, TERM_NO);
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	return 1;
      }
    }

    for (i = optind; i < inargc; i++) {
      if ((new_outbuffer = mstrcat(slvals.outbuffer, inargv[i], &outbuf_len, 0)) == NULL) {
	send_status(slvals.n_sockfd, 112, TERM_NO);
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	return 1;
      }
      else {
	slvals.outbuffer = new_outbuffer;
      }
      if ((new_outbuffer = mstrcat(slvals.outbuffer, " ", &outbuf_len, 0)) == NULL) {
	send_status(slvals.n_sockfd, 112, TERM_NO);
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	return 1;
      }
      else {
	slvals.outbuffer = new_outbuffer;
      }
    }
  }

  /* assemble command */
  strcat(cmd_buffer, " -u ");
  strcat(cmd_buffer, username);
  if (strlen(passwd) > 0) {
    strcat(cmd_buffer, " -w ");
    strcat(cmd_buffer, scrambled_passwd);
  }
  if (n_remove) {
    strcat(cmd_buffer, " -r");
  }
  strcat(cmd_buffer, " -d ");
  strcat(cmd_buffer, db);

  if (n_cgi) {
    strcat(cmd_buffer, " -t cgi ");
  }

  if (*listname) {
    strcat (cmd_buffer, " -b ");
    strcat (cmd_buffer, listname);
  }

  sprintf(cmd_buffer+strlen(cmd_buffer), " %d", strlen(slvals.outbuffer)+TERM_LEN); 

/*    printf("outbuffer:%s<<\ncmd_buffer:%s<<\n", outbuffer, cmd_buffer); */

/*    delete_all_lilimem(&sentinel); */
/*    return 0; */

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

  numbyte = 0;

  /* read acknowledgement from application server */
  if ((cs_status = read_status(slvals.n_sockfd)) != 0) {
    fprintf(errstream, "%s\n", get_status_msg(cs_status));
    close(slvals.n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }

  getsimplelist(&slvals, 1);

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

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_selectdb(): select a database

  int com_selectdb 0 if successful, 1 if error 

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

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_selectdb (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] = "";
  FILE* errstream;
  struct liliform* ptr_current;
  int retval = 0;

  errstream = (n_cgi) ? stdout : stderr;

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

  if (n_batchmode) { /* this does not make sense in batch mode */
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "not a valid command in batchmode. use the -d option instead\n");
    return 0;
  }

  if (strncmp(arg, "-h", 2) == 0) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "Selects the specified database as the default database\nSyntax: selectdb [-h] {db}\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;
  }

  if (n_cgi) {
    if ((ptr_current = get_liliform((struct liliform*)arg, "selectdb")) != NULL) {
      strcat(slvals.outbuffer, ptr_current->value);
    }
  }
  else {
    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);
  }

  if (n_cgi) {
    strcat(slvals.outbuffer, " -t cgi");
  }

  LOG_PRINT(LOG_DEBUG, slvals.outbuffer);
  if ((retval = getsimplelist(&slvals, 1)) == 0) {
    strcpy(current_db, arg);
  }

  close(slvals.n_sockfd);

  return retval;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_getref(): view references

  int com_getref 0 if successful, 1 if error 

  char *arg the spec of the reference which are to be viewed
            In the case of a cgi query this ptr will be cast to
	    struct liliform*

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_getref (char* arg)
{
  return getref(arg, 1);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_countref(): count references

  int com_countref 0 if successful, 1 if error 

  char *arg the spec of the reference which are to be viewed
            In the case of a cgi query this ptr will be cast to
	    struct liliform*

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_countref (char* arg)
{
  return getref(arg, 0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  getref(): view or count references

  int getref 0 if successful, 1 if error 

  char *arg the spec of the reference which are to be viewed
            In the case of a cgi query this ptr will be cast to
	    struct liliform*

  int send_data if set to 1, send back references (getref)
                if set to 0, count references (countref)

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int getref (char* arg, int send_data)
{
  char cmd_buffer[OUTBUF_LEN] = "";
  char* infile;
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char format_string[MAX_FMT_LEN] = "";
  char sort_string[MAX_FMT_LEN] = "";
  char db[_POSIX_PATH_MAX] = "";
  char pdf_root[_POSIX_PATH_MAX] = "";
  char my_toencoding[PREFS_BUF_LEN] = "";
  char listname[PREFS_BUF_LEN] = "";
  char limit[PREFS_BUF_LEN] = "";
  char namespace[PREFS_BUF_LEN] = "";
  char type_string[10] = "";
  char **inargv; /* tokens of the argument */
  char *newarg;
  char *read_result;
  char *new_outbuffer;
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int result;
  int n_frequency = 0;
  int i;
  int n_opt;
  int n_done;
  int n_read_file = 0; /* indicates -f switch */
  int n_cmdlinerror = 0;
  int numbyte;
  int cs_status;
  size_t byte_written = 0;
  size_t outbuf_len;
  FILE* infilefp;
  FILE* errstream;
  FILE* pagerfp; /* ptr to file */
  struct lilimem sentinel;
  struct simplelistvals slvals;

  errstream = (n_cgi) ? stdout : stderr;

   /* get us some buffer for output */
  outbuf_len = 256; /* something to start with */
  slvals.outbuffer = malloc(outbuf_len); 
  if (slvals.outbuffer == NULL
      || insert_lilimem(&sentinel, (void**)&(slvals.outbuffer), NULL)) {
    free(slvals.outbuffer);
    return 1;
  }
  slvals.outbuffer[0] = '\0';

  if (!send_data) {
    strcpy(cmd_buffer, "countref ");
  }
  else {
    strcpy(cmd_buffer, "getref ");
  }

  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';

  if (*pdfroot) {
    strcpy(pdf_root, pdfroot);
  }

  strcpy(db, current_db); /* use default db if set */
  strcpy(my_toencoding, toencoding); /* use default encoding if set */

  /* 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)+8));
  if (newarg == NULL) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

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

  strcpy(newarg, "getref ");
  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]); */
/*   } */

/*   return 0; */

  /* get options */
  optind = 0;

  while ((n_opt = getopt(inargc, inargv, "aA:b:c:C:d:e:E:f:F:g:G:hi:kl:L:n:N:o:O:p:PqQr:R:s:S:t:T:u:U:vVw:")) != -1) {
    switch(n_opt) {
    case 'b':
      strncpy(listname, optarg, PREFS_BUF_LEN);
      listname[PREFS_BUF_LEN-1] = '\0';
      break;
    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 'd':
      /*        printf("-d %s\n", optarg); */
      strncpy(db, optarg, _POSIX_PATH_MAX - 1); /* override preset db */
      db[_POSIX_PATH_MAX-1] = '\0';  /* terminate in case string got truncated */
      break;
    case 'E':
      strncpy(my_toencoding, optarg, PREFS_BUF_LEN);
      my_toencoding[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'f':
      /*        printf("-f %s\n", optarg); */
      if (!strcmp(optarg, "stdin")) {
	n_read_stdin = 1;
      }
      else {
	infile = canonicalize_path(optarg);
	if (insert_lilimem(&sentinel, (void**)&infile, NULL)) {
	  delete_all_lilimem(&sentinel);
	  return 1;
	}
	n_read_file = 1;
      }
      break;
    case 'G':
      strncpy(css_url, optarg, PREFS_BUF_LEN);
      css_url[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'h':
      if (!send_data) {
	printf("Counts the references of a database search.\nSyntax: countref  [-b listname] [-c command] [-d database] [-h] [-N limit:offset] {search-string|-f infile}\nSearch-string: {:XY:{<|<=|=|~|!=|<>|!~|>|>=}{string|regexp}} [AND|OR|AND NOT] [...]\nwhere XY specifies the field to search in\nOptions: -b listname  limit search to the specified personal reference list\n         -c command   pipe the output through command\n         -d database  specify the database to work with\n         -h           prints this mini-help\n         -N limit:offset specify range of datasets\n         -f infile    use the saved search line in file infile\n         All other arguments are interpreted as the search string.\n");
      }
      else {
	printf("Displays the result of a database search.\nSyntax: getref  [-b listname] [-c command] [-d database] [-E encoding] [-G cssfile] [-h] [-n namespace-prefix] [-N limit:offset] [-o outfile] [-O outfile] [-R pdfroot] [-s format] [-S tag] [-t output-format] {search-string|-f infile}\nSearch-string: {:XY:{<|<=|=|~|!=|<>|!~|>|>=}{string|regexp}} [AND|OR|AND NOT] [...]\nwhere XY specifies the field to search in\nOptions: -b listname  limit search to the specified personal reference list\n         -c command   pipe the output through command\n         -d database  specify the database to work with\n         -E encoding  set the output character encoding\n         -G cssfile   specify the CSS stylesheet for (x)html output\n         -h           prints this mini-help\n         -n namespace-prefix set optional namespace prefix for XML output\n         -N limit:offset specify range of datasets\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         -R           use pdfroot as root for path of pdf files\n         -s format    specify fields for screen or style for DocBook output\n         -S tag       sort output by tag ID (default) or PY\n         -t output-format display as format scrn, html, xhtml, db31, db31x, db50x, teix, ris, risx, mods or bibtex\n         -f infile    use the saved search line in file infile\n         All other arguments are interpreted as the search string.\n");
      }
      delete_all_lilimem(&sentinel);
      return 0;
      break;
    case 'n':
      strncpy(namespace, optarg, PREFS_BUF_LEN);
      namespace[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'N':
      strncpy(limit, optarg, PREFS_BUF_LEN);
      limit[PREFS_BUF_LEN-1] = '\0';
      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 'Q':
      n_frequency = 1;
      break;
    case 'R':
      strcpy(pdf_root, optarg);
      break;
    case 's':
      /*        printf("-s %s\n", optarg); */
      strncpy(format_string, optarg, MAX_FMT_LEN - 1);
      format_string[MAX_FMT_LEN-1] = '\0';  /* terminate in case string got truncated */
      break;
    case 'S':
      /*        printf("-S %s\n", optarg); */
      strncpy(sort_string, optarg, MAX_FMT_LEN - 1);
      sort_string[MAX_FMT_LEN-1] = '\0';  /* terminate in case string got truncated */
      break;
    case 't':
      /*        printf("-t %s\n", optarg); */
      strncpy(type_string, optarg, 9);
      type_string[9] = '\0';  /* terminate in case string got truncated */
      break;
      /* now all the options that main has already taken care of */
    case 'a': /* fall through - nothing to do */
    case 'A':
    case 'C':
    case 'e':
    case 'F':
    case 'g':
    case 'i':
    case 'k':
    case 'l':
    case 'L':
    case 'p':
    case 'P':
    case 'q':
    case 'r':
    case 'T':
    case 'u':
    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 (!*db) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "Don't know which database to use. Select one with selectdb or use the -d switch with getref.Stop.\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (n_cmdlinerror) {
    delete_all_lilimem(&sentinel);
    return 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)) {
    delete_all_lilimem(&sentinel);
    close(slvals.n_sockfd);
    return 1;
  }

  /* todo: read securely from a file without buffer overflows
     resize string as needed */
  if (n_read_file || n_read_stdin || optind == inargc) {
    char filebuffer[COMMAND_INBUF_LEN];

    if (n_read_file) {
      infilefp = fopen(infile, "rb");
      if (infilefp == NULL) {
	send_status(slvals.n_sockfd, 112, TERM_NO);
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	return 1;
      }
    }
    else {
      infilefp = stdin; /* stdin is already open */
      n_read_stdin = 1; /* not set if optind == inargc */
    }

    while ((read_result = fgets(filebuffer, COMMAND_INBUF_LEN, infilefp)) != NULL) {
      if ((new_outbuffer = mstrcat(slvals.outbuffer, filebuffer, &outbuf_len, 0)) == NULL) {
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	if (!n_read_stdin) { /* don't close stdin */
	  fclose(infilefp);
	}
	return 1;
      }
      else {
	slvals.outbuffer = new_outbuffer;
      }
    }

    if (ferror(infilefp)) {
      send_status(slvals.n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(slvals.n_sockfd);
      return 1;
    }
    if (!n_read_stdin) { /* don't close stdin */
      fclose(infilefp);
    }
  }
  else {
    if (optind < inargc) {
      int open_quote = 0;

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

	if (open_quote) {
	  if ((new_outbuffer = mstrcat(slvals.outbuffer, "\'", &outbuf_len, 0)) == NULL) {
	    delete_all_lilimem(&sentinel);
	    close(slvals.n_sockfd);
	    return 1;
	  }
	  else {
	    slvals.outbuffer = new_outbuffer;
	  }

	  open_quote--;
	}
	/* the tokenizer returns a quoted item as a separate token
	   even if there is no space between e.g. a '=' and the
	   quoted item. To rectify this, check whether the previous
	   item ended with a '=' or a '~' */
	if (slvals.outbuffer[strlen(slvals.outbuffer)-1] != '='
	    && slvals.outbuffer[strlen(slvals.outbuffer)-1] != '~') {
	  if ((new_outbuffer = mstrcat(slvals.outbuffer, " ", &outbuf_len, 0)) == NULL) {
	    delete_all_lilimem(&sentinel);
	    close(slvals.n_sockfd);
	    return 1;
	  }
	  else {
	    slvals.outbuffer = new_outbuffer;
	  }
	}
	else {
	  /* insert a quote after the '='. This will put back the
	     quote only for items that actually were quoted */
	  if ((new_outbuffer = mstrcat(slvals.outbuffer, "\'", &outbuf_len, 0)) == NULL) {
	    delete_all_lilimem(&sentinel);
	    close(slvals.n_sockfd);
	    return 1;
	  }
	  else {
	    slvals.outbuffer = new_outbuffer;
	  }

	  open_quote++;
	}
      }
      
      slvals.outbuffer[strlen(slvals.outbuffer)-1] = '\0'; /* remove trailing space */
    }
  }

  /* make sure there's no newlines in the string; reuse read_result */
  for (read_result = slvals.outbuffer; *read_result; read_result++) {
    if (*read_result == '\n' || *read_result == '\r') {
      *read_result = ' ';
    }
  }

  /* assemble command string for refdbd */
  strcat(cmd_buffer, " -u ");
  strcat(cmd_buffer, username);
  if (*passwd) {
    strcat(cmd_buffer, " -w ");
    strcat(cmd_buffer, scrambled_passwd);
  }
  strcat(cmd_buffer, " -d ");
  strcat(cmd_buffer, db);

  if (*format_string || *default_fields) {
    strcat(cmd_buffer, " -s \"");
    if (*format_string) {
      strcat(cmd_buffer, format_string);
    }
    else {
      strcat(cmd_buffer, default_fields);
    }
    strcat(cmd_buffer, "\"");
  }

  if (*type_string) {
    strcat(cmd_buffer, " -t \"");
    strcat(cmd_buffer, type_string);
    strcat(cmd_buffer, "\"");
  }

  if (*sort_string) {
    strcat(cmd_buffer, " -S \"");
    strcat(cmd_buffer, sort_string);
    strcat(cmd_buffer, "\"");
  }

  if (*limit) {
    strcat(cmd_buffer, " -N \"");
    strcat(cmd_buffer, limit);
    strcat(cmd_buffer, "\"");
  }

  if (*namespace) {
    strcat(cmd_buffer, " -n \"");
    strcat(cmd_buffer, namespace);
    strcat(cmd_buffer, "\"");
  }

  if (*pdf_root) {
    strcat(cmd_buffer, " -R ");
    strcat(cmd_buffer, pdf_root);
  }
  
  if (*my_toencoding) {
    strcat(cmd_buffer, " -E ");
    strcat(cmd_buffer, my_toencoding);
  }
  
  if (*listname) {
    strcat(cmd_buffer, " -b ");
    strcat(cmd_buffer, listname);
  }

  if (*css_url) {
    strcat(cmd_buffer, " -G ");
    strcat(cmd_buffer, css_url);
  }

  if (n_frequency) {
    strcat(cmd_buffer, " -Q ");
  }

  sprintf(cmd_buffer+strlen(cmd_buffer), " %d", strlen(slvals.outbuffer)+TERM_LEN); 

  LOG_PRINT(LOG_DEBUG, cmd_buffer);

  errstream = (n_cgi) ? stdout : stderr;

  send_status(slvals.n_sockfd, 0, TERM_NO);

  numbyte = tiwrite(slvals.n_sockfd, cmd_buffer, TERM_YES);
  if (numbyte == -1) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "could not write to refdbd. Stop\n");
    return 1;
  }

  numbyte = 0;

  if ((cs_status = read_status(slvals.n_sockfd)) != 0) {
    fprintf(errstream, "%s\n", get_status_msg(cs_status));
    close(slvals.n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }

  send_status(slvals.n_sockfd, 0, TERM_NO);
  numbyte = tiwrite(slvals.n_sockfd, slvals.outbuffer, TERM_YES);
  LOG_PRINT(LOG_DEBUG, cmd_buffer);
  if (numbyte == -1) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "could not write to refdbd. Stop\n");
    close(slvals.n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }

  /* openpager and open_outfile are guaranteed to return a valid file/pipe - 
     and be it stdout */
  if (slvals.n_file_open) {
    pagerfp = open_outfile(slvals.outfile, 0);
  }
  else if (slvals.n_file_append) {
    pagerfp = open_outfile(slvals.outfile, 1);
  }
  else if (slvals.n_pipe) {
    pagerfp = openpager(slvals.outpipe);
  }
  else {
    pagerfp = openpager(the_pager);
  }

  n_done = 0;

  do { /* loop until we have all requested datasets */
    int n_error;

    cs_status = read_status(slvals.n_sockfd);

    if (cs_status != 402 /* last dataset */
	&& cs_status != 404) { /* finished dataset */
      cgi_header(CGI_PLAIN);
      fprintf(errstream, "%s\n", get_status_msg(cs_status));
      if (slvals.n_file_open || slvals.n_file_append) {
	close_outfile(pagerfp);
      }
      else {
	closepager(pagerfp);
      }
      n_broken_pipe = 0;
      delete_all_lilimem(&sentinel);
      return 1;
    }
  
    if (cs_status == 402) {
      n_done++;
    }

    byte_written += read_terminated_string(&slvals, pagerfp, &n_error);

    if (n_error) {
      delete_all_lilimem(&sentinel);
      if (slvals.n_file_open || slvals.n_file_append) {
	close_outfile(pagerfp);
      }
      else {
	closepager(pagerfp);
      }
      n_broken_pipe = 0;
      return 1;
    }

  } while (!n_done);

/*   printf("trying to read summary\n"); */
  /* read summary */
  /*   send_status(slvals.n_sockfd, 0, TERM_NO); */
  cs_status = read_status(slvals.n_sockfd);
	
  if (cs_status != 0 /* success */
      && cs_status != 803) { /* partial success */
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "%s\n", get_status_msg(cs_status));
    delete_all_lilimem(&sentinel);
    return 1;
  }
	
  numbyte = tread(slvals.n_sockfd, slvals.inbuffer, OUTBUF_LEN);
  if (numbyte == -1) {
    fprintf(stderr, "%s\n", get_status_msg(109));
    n_broken_pipe = 0;
    delete_all_lilimem(&sentinel);
    return 1;
  }
	
  send_status(slvals.n_sockfd, 0, TERM_NO);
      
  if (slvals.n_file_open || slvals.n_file_append) {
    close_outfile(pagerfp);
    fprintf(errstream, "%d byte written to %s\n", byte_written, slvals.outfile);
  }
  else {
    closepager(pagerfp);
  }

  close(slvals.n_sockfd);

  fprintf(errstream, "%s", slvals.inbuffer);

  delete_all_lilimem(&sentinel);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_getau(): get a list of authors

  int com_getau 0 if successful, 1 if error 

  char *arg the spec of the keywords that are to be retrieved

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_getau (char* arg)
{
  return getfoo(arg, GETAU);
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_getax(): get a list of any level authors

  int com_getax 0 if successful, 1 if error 

  char *arg the spec of the names that are to be retrieved

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_getax (char* arg)
{
  return getfoo(arg, GETAX);
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_geted(): get a list of editors

  int com_getau 0 if successful, 1 if error 

  char *arg the spec of the keywords that are to be retrieved

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_geted (char* arg)
{
  return getfoo(arg, GETED);
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_getas(): get a list of series authors

  int com_getau 0 if successful, 1 if error 

  char *arg the spec of the keywords that are to be retrieved

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_getas (char* arg)
{
  return getfoo(arg, GETAS);
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_getkw(): get a list of keywords

  int com_getkw 0 if successful, 1 if error 

  char *arg the spec of the keywords that are to be retrieved

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_getkw (char* arg)
{
  return getfoo(arg, GETKW);
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_getjo(): get a list of journal names

  int com_getjo 0 if successful, 1 if error 

  char *arg the spec of the journals that are to be retrieved

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_getjo (char* arg)
{
  return getfoo(arg, GETJO);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_getjf(): get a list of journal names (full)

  int com_getjf 0 if successful, 1 if error 

  char *arg the spec of the journals that are to be retrieved

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_getjf (char* arg)
{
  return getfoo(arg, GETJF);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_getj1(): get a list of journal names (custabbrev1)

  int com_getj1 0 if successful, 1 if error 

  char *arg the spec of the journals that are to be retrieved

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_getj1 (char* arg)
{
  return getfoo(arg, GETJ1);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_getj2(): get a list of journal names (custabbrev2)

  int com_getj2 0 if successful, 1 if error 

  char *arg the spec of the journals that are to be retrieved

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_getj2 (char* arg)
{
  return getfoo(arg, GETJ2);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  getfoo(): get a list of authors | journals | keywords

  int getfoo 0 if successful, 1 if error 

  char *arg the spec of the elements that are to be retrieved

  int type 0 = authors, 1 = keywords, 2 = journals, 3 = journals (full)
           4 = journals (custabbrev1), 5 = journals (custabbrev2),
           6 = editors, 7 = series authors, 8 = any authors

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int getfoo (char* arg, int type)
{
  char outbuffer[COMMAND_INBUF_LEN]; /* holds the command for the server */
  char outfile[_POSIX_PATH_MAX] = "";
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char db[_POSIX_PATH_MAX] = "";
  char my_toencoding[PREFS_BUF_LEN] = "";
  char my_format[PREFS_BUF_LEN] = "";
  char limit[PREFS_BUF_LEN] = "";
  char **inargv; /* tokens of the argument */
  char *newarg;
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int result;
  int i;
  int n_opt;
  int n_cmdlinerror = 0;
  int n_all = 0; /* if 1, display all synonyms of a journal search */
  FILE* errstream;
  struct lilimem sentinel;
  struct simplelistvals slvals;
  struct liliform* ptr_current;

  errstream = (n_cgi) ? stdout : stderr;

  strcpy(db, current_db); /* use default db if set */
  strcpy(my_toencoding, toencoding); /* used default encoding if set */

  slvals.outbuffer = outbuffer;

  slvals.n_file_open = 0;
  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = outfile;
  slvals.outpipe = NULL;

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

  if (n_cgi) {
#ifdef REFDB_CGI
    ptr_current = get_liliform((struct liliform*)arg, "kajqueryfield");
    if (ptr_current) {
      if (!strcmp(ptr_current->value, "keyword")) {
	strcpy(slvals.outbuffer, "getkw ");
	if ((ptr_current = get_liliform((struct liliform*)arg, "kajquery")) != NULL) {
	  strcat(slvals.outbuffer, ptr_current->value);
	}
      }
      else if (!strcmp(ptr_current->value, "author")) {
	strcpy(slvals.outbuffer, "getau ");
	if ((ptr_current = get_liliform((struct liliform*)arg, "kajquery")) != NULL) {
	  strcat(slvals.outbuffer, ptr_current->value);
	}
      }
      else if (!strcmp(ptr_current->value, "editor")) {
	strcpy(slvals.outbuffer, "geted ");
	if ((ptr_current = get_liliform((struct liliform*)arg, "kajquery")) != NULL) {
	  strcat(slvals.outbuffer, ptr_current->value);
	}
      }
      else if (!strcmp(ptr_current->value, "seditor")) {
	strcpy(slvals.outbuffer, "getas ");
	if ((ptr_current = get_liliform((struct liliform*)arg, "kajquery")) != NULL) {
	  strcat(slvals.outbuffer, ptr_current->value);
	}
      }
      else if (!strcmp(ptr_current->value, "journalfull")) {
	strcpy(slvals.outbuffer, "getjf ");
	if ((ptr_current = get_liliform((struct liliform*)arg, "kajquery")) != NULL) {
	  strcat(slvals.outbuffer, ptr_current->value);
	}
      }
      else if (!strcmp(ptr_current->value, "journalabbrev")) {
	strcpy(slvals.outbuffer, "getjo ");
	if ((ptr_current = get_liliform((struct liliform*)arg, "kajquery")) != NULL) {
	  strcat(slvals.outbuffer, ptr_current->value);
	}
      }
      else if (!strcmp(ptr_current->value, "journalcustabbrev1")) {
	strcpy(slvals.outbuffer, "getj1 ");
	if ((ptr_current = get_liliform((struct liliform*)arg, "kajquery")) != NULL) {
	  strcat(slvals.outbuffer, ptr_current->value);
	}
      }
      else if (!strcmp(ptr_current->value, "journalcustabbrev2")) {
	strcpy(slvals.outbuffer, "getj2 ");
	if ((ptr_current = get_liliform((struct liliform*)arg, "kajquery")) != NULL) {
	  strcat(slvals.outbuffer, ptr_current->value);
	}
      }
    }
#endif
  }
  else { /* not cgi */
/*     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 tokens, 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)+7));
      if (newarg == NULL) {
	delete_all_lilimem(&sentinel);
	return 1;
      }

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

      if (type == GETKW) {
	strcpy(newarg, "getkw ");
      }
      else if (type == GETJO) {
	strcpy(newarg, "getjo ");
      }
      else if (type == GETJF) {
	strcpy(newarg, "getjf ");
      }
      else if (type == GETJ1) {
	strcpy(newarg, "getj1 ");
      }
      else if (type == GETJ2) {
	strcpy(newarg, "getj2 ");
      }
      else if (type == GETED) {
	strcpy(newarg, "geted ");
      }
      else if (type == GETAS) {
	strcpy(newarg, "getas ");
      }
      else if (type == GETAX) {
	strcpy(newarg, "getax ");
      }
      else { /* default is author */
	strcpy(newarg, "getau ");
      }
      strcat(newarg, arg);
      
      result = cmdln_tokenize(&inargc, &inargv, inargcmax, newarg);

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

    if (type == GETKW) {
      strcpy(slvals.outbuffer, "getkw ");
    }
    else if (type == GETJO) {
      strcpy(slvals.outbuffer, "getjo ");
    }
    else if (type == GETJF) {
      strcpy(slvals.outbuffer, "getjf ");
    }
    else if (type == GETJ1) {
      strcpy(slvals.outbuffer, "getj1 ");
    }
    else if (type == GETJ2) {
      strcpy(slvals.outbuffer, "getj2 ");
    }
    else if (type == GETED) {
      strcpy(slvals.outbuffer, "geted ");
    }
    else if (type == GETAS) {
      strcpy(slvals.outbuffer, "getas ");
    }
    else if (type == GETAX) {
      strcpy(slvals.outbuffer, "getax ");
    }
    else { /* default is author */
      strcpy(slvals.outbuffer, "getau ");
    }

    /* 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, "aA:c:C:d:e:E:f:F:g:G:hi:kl:L:N:o:O:p:Pqr:R:s:S:t:T:u:U:vVw:")) != -1) {
      switch(n_opt) {
      case 'a':
	n_all = 1;
	break;
      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 'd':
	/*        printf("-d %s\n", optarg); */
	strncpy(db, optarg, _POSIX_PATH_MAX-1); /* override preset db */
	db[_POSIX_PATH_MAX-1] = '\0';
	break;
      case 'E':
	strncpy(my_toencoding, optarg, PREFS_BUF_LEN);
	my_toencoding[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'h':
	if (type == 0 || type == 6 || type == 7) {
	  printf("Displays the result of an author search.\nSyntax: getau [-c command] [-d database] [-h] [-N limit:offset] [-o outfile] [-O outfile] {regexp}\ngeted and getas work the same way, but return a list of editors and series authors, respectively.\nOptions: -c command   pipe the output through command\n         -d database  specify the database to work with\n         -h           prints this mini-help\n         -N limit:offset specify the range of datasets\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         -s format    output format (freq|relfreq)\n         All other arguments are interpreted as the search string/regular expression.\n");
	}
	else if (type == 1) {
	  printf("Displays the result of a keyword search.\nSyntax: getkw [-c command] [-d database] [-h] [-N limit:offset] [-o outfile] [-O outfile] {regexp}\nOptions: -c command   pipe the output through command\n         -d database  specify the database to work with\n         -h           prints this mini-help\n         -N limit:offset specify range of datasets\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         -s format    output format (freq|relfreq)\n         All other arguments are interpreted as the search string/regular expression.\n");
	}
	else {
	  printf("Displays the result of a journal search.\nSyntax: getjo [-a] [-c command] [-d database] [-h] [-N limit:offset] [-o outfile] [-O outfile] {regexp}\ngetjf, getj1, getj2 are used the same way, but search in full journal names, custom abbreviations1, and custom abbreviations 2, respectively\nOptions: -a list all synonyms\n         -c command   pipe the output through command\n         -d database  specify the database to work with\n         -h           prints this mini-help\n         -N limit:offset specify range of datasets\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         -s format    output format (freq|relfreq)\n         All other arguments are interpreted as the search string/regular expression.\n");
	}
	
	delete_all_lilimem(&sentinel);
	return 0;
	break;
      case 'N':
	strncpy(limit, optarg, PREFS_BUF_LEN);
	limit[PREFS_BUF_LEN-1] = '\0';
	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;
	/* now all the options that main has already taken care of */
      case 's':
	strncpy(my_format, optarg, PREFS_BUF_LEN-1);
	my_format[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'A':
      case 'C':
      case 'e': /* fall through - nothing to do */
      case 'f':
      case 'F':
      case 'g':
      case 'G':
      case 'i':
      case 'k':
      case 'l':
      case 'L':
      case 'p':
      case 'P':
      case 'q':
      case 'r':
      case 'R':
      case 'S':
      case 't':
      case 'T':
      case 'u':
      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 (!*db) {
    fprintf(stderr, "Don't know which database to use. Select one with selectdb or use the -d switch with getau.Stop.\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (n_cmdlinerror) {
    delete_all_lilimem(&sentinel);
    return 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 (!n_cgi && inargc > optind) { /* only if there are arguments */
    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 space */
  }
  else if (n_cgi) {
    strcat(slvals.outbuffer, " -t cgi"); /* request cgi output */
  }


  if (n_all || n_cgi) {
    strcat(slvals.outbuffer, " -a");
  }

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

  if (*my_format) {
    strcat(slvals.outbuffer, " -s ");
    strcat(slvals.outbuffer, my_format);
  }

  if (*my_toencoding) {
    strcat(slvals.outbuffer, " -E ");
    strcat(slvals.outbuffer, my_toencoding);
  }

  if (*limit) {
    strcat(slvals.outbuffer, " -N ");
    strcat(slvals.outbuffer, limit);
  }

  strcat(slvals.outbuffer, " -d ");
  strcat(slvals.outbuffer, db);

  LOG_PRINT(LOG_DEBUG, slvals.outbuffer);
  getsimplelist(&slvals, 1);

  close(slvals.n_sockfd);

  delete_all_lilimem(&sentinel);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  addref(): adds, replaces, or checks references

  static int addref  0 if successful, 1 on error

  char *arg the name(s) of the file(s) containing the references

  int update_ref if 1, the references will be replaced in the db;
            if 0, the references will be added
	    if 2, the references will be checked w/o adding them

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int addref (char* arg, int update_ref)
{
  char srv_inbuffer[COMMAND_INBUF_LEN] = "";
  char my_fromencoding[PREFS_BUF_LEN] = "";
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  char db[_POSIX_PATH_MAX+1] = ""; /* the database name */
  char my_reftype[PREFS_BUF_LEN]; /* input data type (ris|risx) */
  char my_outtype[PREFS_BUF_LEN]; /* checkref output data type (screen|html) */
  char format_string[MAX_FMT_LEN] = ""; /* additional fields for output */
  char check_string[MAX_FMT_LEN] = ""; /* fields to check */
  char set_owner[32] = "";
  char outfile[_POSIX_PATH_MAX] = "";
  char *ris_set_buffer = NULL;
  char **inargv; /* tokens of the argument */
  char *newarg = NULL;
  int numbyte;
  int numcycles; /* number of cycles reading input */
  int n_read_done = 0;
  int n_setcount = 0;
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int n_opt;
  int n_cmdlinerror = 0;
  int result;
  int n_personal = 0;
  int n_keep_id = 0; /* indicates -k switch */
  int n_send_result;
  int k;
  int cs_status;
  int n_error;
  int num_trailz;
  size_t n_setlength = 1;
  size_t byte_written_to_inbuffer = 0;
  size_t byte_written = 0;
  FILE *pagerfp;
  FILE *addresultfp; /* if using output other than screen, send addresult info here */
  FILE *infp = NULL;
  FILE* errstream;
  struct simplelistvals slvals;
  struct lilimem sentinel;
  struct liliform* ptr_current;

  errstream = (n_cgi) ? stdout : stderr;

  slvals.n_file_open = 0;

  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = outfile;
  slvals.outpipe = NULL;

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

  strcpy(db, current_db); /* use default db if set */
  strcpy(my_reftype, reftype);
  strcpy(my_outtype, outtype);
  strcpy(my_fromencoding, fromencoding); /* use default encoding if set */

  if (n_cgi) {
#ifdef REFDB_CGI
    if (update_ref) {
      ptr_current = get_nliliform((struct liliform*)arg, "updateid", 8);
    }
    else {
      ptr_current = get_liliform((struct liliform*)arg, "addref");
    }
    if (ptr_current != NULL) {
      ris_set_buffer = ptr_current->value;
      n_setlength = strlen(ris_set_buffer) + 1;
    }
#endif /* REFDB_CGI */
  }
  else { /* if not cgi */
      /* 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)+8));
      if (newarg == NULL) {
	delete_all_lilimem(&sentinel);
	return 1;
      }

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

      strcpy(newarg, "addref ");
      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;
    
    if (update_ref == 1) { /* updateref */

      while ((n_opt = getopt(inargc, inargv, "aA:c:C:d:e:E:f:F:g:G:hi:kl:L:o:O:p:Pqr:R:s:S:t:T:u:U:vVw:")) != -1) {
	switch(n_opt) {
	case 'A':
	  strncpy(my_reftype, optarg, PREFS_BUF_LEN - 1);
	  my_reftype[PREFS_BUF_LEN - 1] = '\0';
	  break;
	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 'd':
	  /*  	printf("-d %s\n", optarg); */
	  strncpy(db, optarg, _POSIX_PATH_MAX-1); /* override preset db */
	  db[_POSIX_PATH_MAX-1] = '\0';
	  break;
	case 'E':
	  strncpy(my_fromencoding, optarg, PREFS_BUF_LEN);
	  my_fromencoding[PREFS_BUF_LEN-1] = '\0';
	  break;
	case 'f':
	  /*        printf("-f %s\n", optarg); */
	  if (!strcmp(optarg, "stdin")) {
	    n_read_stdin = 1;
	  }
	  break;
	case 'G':
	  strncpy(css_url, optarg, PREFS_BUF_LEN);
	  css_url[PREFS_BUF_LEN-1] = '\0';
	  break;
	case 'h':
	  delete_all_lilimem(&sentinel);
	  fprintf(errstream, "Replaces the specified references in the database\nSyntax: updateref [-A type] [-c command] [-d database] [-E encoding] [-h] [-o outfile] [-O outfile] [-P] [-U username] {file|-f infile}\nOptions: -A type      input data type (ris|risx), ris is default\n         -c command   pipe the output through command\n         -d database  specify the database to work with\n         -E encoding  specify the input character encoding\n         -f infile    Read the names of the files with the references from file infile\n         -h           prints this mini-help\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         -P update only personal information (AV, N1, RP)\n         -U username  specify a different username than the current user\n         All other arguments are interpreted as filenames.\n");
	  return 0;
	  break;
	case 'o':
	  /*        printf("-o %s\n", optarg); */
	  strncpy(slvals.outfile, optarg, _POSIX_PATH_MAX - 1);
	  slvals.outfile[_POSIX_PATH_MAX-1] = '\0';  /* terminate in case string got truncated */
	  slvals.n_file_open = 1;
	  break;
	case 'O':
	  /*        printf("-O %s\n", optarg); */
	  strncpy(slvals.outfile, optarg, _POSIX_PATH_MAX - 1);
	  slvals.outfile[_POSIX_PATH_MAX-1] = '\0';  /* terminate in case string got truncated */
	  slvals.n_file_append = 1;
	  break;
	case 'P':
	  n_personal = 1;
	  break;
	case 'U':
	  /*  	printf("-U %s\n", optarg); */
	  strcpy(set_owner, optarg);
	  break;
	  /* now all the options that main has already taken care of */
	case 'a': /* fall through - nothing to do */
	case 'C':
	case 'e':
	case 'F':
	case 'g':
	case 'i':
	case 'k':
	case 'l':
	case 'L':
	case 'p':
	case 'q':
	case 'r':
	case 'R':
	case 'S':
	case 's':
	case 't':
	case 'T':
	case 'u':
	case 'v':
	case 'V':
	case 'w':
	  break;
	case ':':
	  fprintf(errstream, "missing option\n");
	  n_cmdlinerror = 1;
	  break;
	case '?':
	  fprintf(errstream, "unknown option\n");
	  n_cmdlinerror = 1;
	  break;
	}
      }
    }
    else if (!update_ref) { /* addref */

      while ((n_opt = getopt(inargc, inargv, "aA:c:C:d:e:E:f:F:g:G:hi:kl:L:o:O:p:Pqr:R:s:S:t:T:u:U:vVw:")) != -1) {
	switch(n_opt) {
	case 'A':
	  strncpy(my_reftype, optarg, PREFS_BUF_LEN - 1);
	  my_reftype[PREFS_BUF_LEN - 1] = '\0';
	  break;
	case 'c':
	  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 'd':
	  /*  	printf("-d %s\n", optarg); */
	  strcpy(db, optarg); /* override preset db */
	  break;
	case 'E':
	  strncpy(my_fromencoding, optarg, PREFS_BUF_LEN);
	  my_fromencoding[PREFS_BUF_LEN-1] = '\0';
	  break;
	case 'f':
	  /*        printf("-f %s\n", optarg); */
	  if (!strcmp(optarg, "stdin")) {
	    n_read_stdin = 1;
	  }
	  break;
	case 'G':
	  strncpy(css_url, optarg, PREFS_BUF_LEN);
	  css_url[PREFS_BUF_LEN-1] = '\0';
	  break;
	case 'h':
	  delete_all_lilimem(&sentinel);
	  fprintf(errstream, "Adds the specified references to the database\nSyntax: addref [-A type] [-c command] [-d database] [-E encoding] [-h] [-k] [-o outfile] [-O outfile] [-U username] {file|-f infile}\nOptions: -A type      input data type (ris|risx), ris is default\n         -c command   pipe the output through command\n         -d database  specify the database to work with\n         -E encoding  specify the input character encoding\n         -f infile    Read the names of the files with the references from file infile\n         -h           prints this mini-help\n         -k           keep reference ID\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         -U username  specify a different username than the current user\n         All other arguments are interpreted as filenames.\n");
	  return 0;
	  break;
	case 'k':
	  n_keep_id = 1;
	  break;
	case 'o':
	  /*        printf("-o %s\n", optarg); */
	  strncpy(slvals.outfile, optarg, _POSIX_PATH_MAX - 1);
	  slvals.outfile[_POSIX_PATH_MAX-1] = '\0';  /* terminate in case string got truncated */
	  slvals.n_file_open = 1;
	  break;
	case 'O':
	  /*        printf("-O %s\n", optarg); */
	  strncpy(slvals.outfile, optarg, _POSIX_PATH_MAX - 1);
	  slvals.outfile[_POSIX_PATH_MAX-1] = '\0';  /* terminate in case string got truncated */
	  slvals.n_file_append = 1;
	  break;
	case 'U':
	  /*  	printf("-U %s\n", optarg); */
	  strcpy(set_owner, optarg);
	  break;
	  /* now all the options that main has already taken care of */
	case 'a':
	case 'C': /* fall through - nothing to do */
	case 'e':
	case 'F':
	case 'g':
	case 'i':
	case 'l':
	case 'L':
	case 'p':
	case 'P':
	case 'q':
	case 'r':
	case 'R':
	case 'S':
	case 's':
	case 't':
	case 'T':
	case 'u':
	case 'v':
	case 'V':
	case 'w':
	  break;
	case ':':
	  fprintf(errstream, "missing option\n");
	  n_cmdlinerror = 1;
	  break;
	case '?':
	  fprintf(errstream, "unknown option\n");
	  n_cmdlinerror = 1;
	  break;
	}
      }
    }
    else { /* checkref */

      while ((n_opt = getopt(inargc, inargv, "aA:c:C:d:e:E:f:F:g:G:hi:kl:L:o:O:p:Pqr:R:s:S:t:T:u:U:vVw:")) != -1) {
	switch(n_opt) {
	case 'A': /* input data type */
	  strncpy(my_reftype, optarg, PREFS_BUF_LEN - 1);
	  my_reftype[PREFS_BUF_LEN - 1] = '\0';
	  break;
	case 'c':
	  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 'd':
	  /*  	printf("-d %s\n", optarg); */
	  strcpy(db, optarg); /* override preset db */
	  break;
	case 'E':
	  strncpy(my_fromencoding, optarg, PREFS_BUF_LEN);
	  my_fromencoding[PREFS_BUF_LEN-1] = '\0';
	  break;
	case 'f':
	  /*        printf("-f %s\n", optarg); */
	  if (!strcmp(optarg, "stdin")) {
	    n_read_stdin = 1;
	  }
	  break;
	case 'G':
	  strncpy(css_url, optarg, PREFS_BUF_LEN);
	  css_url[PREFS_BUF_LEN-1] = '\0';
	  break;
	case 'h':
	  delete_all_lilimem(&sentinel);
	  fprintf(errstream, "Checks the specified references without adding them to the database\nSyntax: checkref [-A type] [-c command] [-d database] [-E encoding] [-G cssfile] [-h] [-k] [-o outfile] [-O outfile] [-r fieldlist] [-s fieldlist] [-t output-type] [-U username] {file|-f infile}\nOptions: -A type      input data type (ris|risx), ris is default\n         -c command   pipe the output through command\n         -d database  specify the database to work with\n         -E encoding  specify the input character encoding\n         -f infile    Read the names of the files with the references from file infile\n         -G cssfile   select the CSS stylesheet to use with the xhtml output\n         -h           prints this mini-help\n         -k           keep reference ID\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         -r fieldlist  select fields for duplicate check\n         -s fieldlist  select additional fields for xhtml display\n         -t output-type select output type (scrn|xhtml)\n         -U username  specify a different username than the current user\n         All other arguments are interpreted as filenames.\n");
	  return 0;
	  break;
	case 'k':
	  n_keep_id = 1;
	  break;
	case 'o':
	  /*        printf("-o %s\n", optarg); */
	  strncpy(slvals.outfile, optarg, _POSIX_PATH_MAX - 1);
	  slvals.outfile[_POSIX_PATH_MAX-1] = '\0';  /* terminate in case string got truncated */
	  slvals.n_file_open = 1;
	  break;
	case 'O':
	  /*        printf("-O %s\n", optarg); */
	  strncpy(slvals.outfile, optarg, _POSIX_PATH_MAX - 1);
	  slvals.outfile[_POSIX_PATH_MAX-1] = '\0';  /* terminate in case string got truncated */
	  slvals.n_file_append = 1;
	  break;
	case 'r':
	  strncpy(check_string, optarg, MAX_FMT_LEN - 1);
	  check_string[MAX_FMT_LEN-1] = '\0';  /* terminate in case string got truncated */
	  break;
	case 's':
	  strncpy(format_string, optarg, MAX_FMT_LEN - 1);
	  format_string[MAX_FMT_LEN-1] = '\0';  /* terminate in case string got truncated */
	  break;
	case 't': /* output data type */
	  strncpy(my_outtype, optarg, PREFS_BUF_LEN - 1);
	  my_outtype[PREFS_BUF_LEN - 1] = '\0';
	  break;
	case 'U':
	  /*  	printf("-U %s\n", optarg); */
	  strcpy(set_owner, optarg);
	  break;
	  /* now all the options that main has already taken care of */
	case 'a':
	case 'C': /* fall through - nothing to do */
	case 'e':
	case 'F':
	case 'g':
	case 'i':
	case 'l':
	case 'L':
	case 'p':
	case 'P':
	case 'q':
	case 'R':
	case 'S':
	case 'T':
	case 'u':
	case 'v':
	case 'V':
	case 'w':
	  break;
	case ':':
	  fprintf(errstream, "missing option\n");
	  n_cmdlinerror = 1;
	  break;
	case '?':
	  fprintf(errstream, "unknown option\n");
	  n_cmdlinerror = 1;
	  break;
	}
      }
    }
  }
	    
  /* get arguments */
/*    { */
/*      int i; */
/*      for (i = optind; i < inargc; i++) { */
/*        printf("1argument %s<<\n", inargv[i]); */
/*      } */
/*    } */

  if (!*db) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "Don't know which database to use. Select one with selectdb or use the -d switch with addref.Stop.\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (n_cmdlinerror) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  /* try to connect to our server */
  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, srv_inbuffer)) {
    delete_all_lilimem(&sentinel);
    close(slvals.n_sockfd);
    return 1;
  }

  if (!n_cgi) {
    /* get us some memory, will be realloc'd by read_ris_set() as needed */
    ris_set_buffer = malloc(1);
    if (insert_lilimem(&sentinel, (void**)&ris_set_buffer, NULL)) {
      send_status(slvals.n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(slvals.n_sockfd);
      return 1;
    }
  }

  /* and more memory for client/server communication */
  slvals.outbuffer = malloc(COMMAND_INBUF_LEN);
  if (insert_lilimem(&sentinel, (void**)&slvals.outbuffer, NULL)) {
    send_status(slvals.n_sockfd, 112, TERM_NO);
    delete_all_lilimem(&sentinel);
    close(slvals.n_sockfd);
    return 1;
  }

  if (update_ref == 1) {
    strcpy(slvals.outbuffer, "updateref ");
  }
  else if (update_ref == 2) {
    strcpy(slvals.outbuffer, "checkref ");
  }
  else {
    strcpy(slvals.outbuffer, "addref ");
  }

  /* assemble and send our command */
  strcat(slvals.outbuffer, " -u ");
  strcat(slvals.outbuffer, username);
  if (*passwd) {
    strcat(slvals.outbuffer, " -w ");
    strcat(slvals.outbuffer, scrambled_passwd);
  }
  strcat(slvals.outbuffer, " -d ");
  strcat(slvals.outbuffer, db);

  strcat(slvals.outbuffer, " -A ");
  strcat(slvals.outbuffer, my_reftype);

  if (*set_owner) {
    strcat(slvals.outbuffer, " -U ");
    strcat(slvals.outbuffer, set_owner);
  }

  if (*my_fromencoding) {
    strcat(slvals.outbuffer, " -E ");
    strcat(slvals.outbuffer, my_fromencoding);
  }

  if (n_personal) {
    strcat(slvals.outbuffer, " -p");
  }

  if (n_keep_id) {
    strcat(slvals.outbuffer, " -k");
  }

  if (*my_outtype) {
    strcat(slvals.outbuffer, " -t ");
    strcat(slvals.outbuffer, my_outtype);
  }

  if (*css_url) {
    strcat(slvals.outbuffer, " -G ");
    strcat(slvals.outbuffer, css_url);
  }

  if (*format_string || *default_fields) {
    strcat(slvals.outbuffer, " -s \"");
    if (*format_string) {
      strcat(slvals.outbuffer, format_string);
    }
    else {
      strcat(slvals.outbuffer, default_fields);
    }
    strcat(slvals.outbuffer, "\"");
  }

  if (*check_string || *check_fields) {
    strcat(slvals.outbuffer, " -c \"");
    if (*check_string) {
      strcat(slvals.outbuffer, check_string);
    }
    else {
      strcat(slvals.outbuffer, check_fields);
    }
    strcat(slvals.outbuffer, "\"");
  }

/*    free(newarg); */
/*    newarg = NULL; */

  LOG_PRINT(LOG_DEBUG, slvals.outbuffer);
  send_status(slvals.n_sockfd, 0, TERM_NO);
  numbyte = tiwrite(slvals.n_sockfd, slvals.outbuffer, TERM_YES);
  /*  printf("%s\n", slvals.outbuffer); */

  if (numbyte == -1) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "could not write to refdbd. Stop\n");
    delete_all_lilimem(&sentinel);
    close(slvals.n_sockfd);
    return 1;
  }
  
  cs_status = read_status(slvals.n_sockfd);

  if (cs_status == 701) {
    /* character conversion could not be initialized */
    fprintf(errstream, "%s\n", get_status_msg(701));
  }
  else if (cs_status) {
    cgi_header(CGI_PLAIN);
    delete_all_lilimem(&sentinel);
    LOG_PRINT(LOG_WARNING, get_status_msg(cs_status));
    close(slvals.n_sockfd);
    return 1;
  }

  /* openpager is guaranteed to return a valid file/pipe - 
     and be it stdout */
  if (slvals.n_file_open) {
    pagerfp = open_outfile(slvals.outfile, 0);
  }
  else if (slvals.n_file_append) {
    pagerfp = open_outfile(slvals.outfile, 1);
  }
  else if (slvals.n_pipe) {
    pagerfp = openpager(slvals.outpipe);
  }
  else {
    pagerfp = openpager(the_pager);
  }

  if (!strcmp(my_outtype, "xhtml")) {
    addresultfp = stderr;
  }
  else {
    addresultfp = pagerfp;
  }

  if (n_read_stdin || optind == inargc) {
    infp = stdin;
    numcycles = 1;
    n_read_stdin = 1; /* not set if optind == inargc */
  }
  else {
    numcycles = inargc-optind;
  }
/*   printf("%d\n", numcycles); */
  for (k = 0; k < numcycles; k++) {
    if (!n_read_stdin && !n_cgi) {
      /* 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 4 of communication protocol */
    if (!strcmp(my_reftype, "ris")) {
/*       printf("call send_ris_data()\n"); */
      n_send_result = send_ris_data(infp, addresultfp, errstream, slvals.n_sockfd, &ris_set_buffer, &n_setlength, &n_setcount, default_ris, n_cgi, &byte_written);
    }
    else {
      n_send_result = send_xml_data(infp, pagerfp, errstream, slvals.n_sockfd, &byte_written);
    }

    if (n_send_result) {
      if (!n_read_stdin) {
	fclose(infp);
      }
      if (slvals.n_file_open || slvals.n_file_append) {
	close_outfile(pagerfp);
      }
      else {
	closepager(pagerfp);
      }
      delete_all_lilimem(&sentinel);
      close(slvals.n_sockfd);
      n_broken_pipe = 0;
      return 1;
    }
  } /* end for */

  /* ------------------------------------------------------------ */
  /* PHASE 5 */
  /*  signal server that we're done */
  send_status(slvals.n_sockfd, 402, TERM_NO); /* #7 */

  /* check server status, should be "chunk added successfully" */
  if ((cs_status = read_status(slvals.n_sockfd)) != 403) { /* #8 */
    if (cs_status == 400) {

      n_read_done = 0;
      /* retrieve server-generated error message */
      do {
	numbyte = tread(slvals.n_sockfd, slvals.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(slvals.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(slvals.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 */

  /* read data */
  byte_written_to_inbuffer = read_terminated_string(&slvals, pagerfp, &n_error);
  /* read summary */
  send_status(slvals.n_sockfd, 0, TERM_NO); /* #9 */
  cs_status = read_status(slvals.n_sockfd); /* #10 */

  if (cs_status != 0 /* success */
      && cs_status != 803) { /* partial success */
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "%s", get_status_msg(cs_status));
    return 1;
  }

  numbyte = tread(slvals.n_sockfd, slvals.inbuffer, OUTBUF_LEN);
  if (numbyte == -1) {
    fprintf(stderr, "could not read from refdbd. Stop\n");
    n_broken_pipe = 0;
    return 1;
  }
      
  send_status(slvals.n_sockfd, 0, TERM_NO); /* #11 */

  /* reset */
  n_broken_pipe = 0;

  if (slvals.n_file_open || slvals.n_file_append) {
    close_outfile(pagerfp);
    if (update_ref == 2) {
      fprintf(errstream, "%d byte written to %s\n", byte_written_to_inbuffer, slvals.outfile);
    }
    else {
      fprintf(errstream, "%d byte written to %s\n", byte_written, slvals.outfile);
    }
  }
  else {
    closepager(pagerfp);
  }

  fprintf(errstream, "%s", slvals.inbuffer);


 cleanup:
/*    printf("%s\n", inbuffer); */
  delete_all_lilimem(&sentinel);
  close(slvals.n_sockfd);
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_whichdb(): displays the currently selected database

  int com_whichdb  0 if successful, 1 on error

  char *arg currently not used

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_whichdb (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] = "";

  if (!*current_db) {
    fprintf(stderr, "No default database. Select one with selectdb. Stop.\n");
    return 1;
  }

  slvals.outbuffer = outbuffer;

  strcpy(slvals.outbuffer, "whichdb");
  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("Prints the name of the current database and some database information\nSyntax: whichdb [-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)) {
    return 1;
  }

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

  LOG_PRINT(LOG_DEBUG, slvals.outbuffer);
  getsimplelist(&slvals, 1);

  close(slvals.n_sockfd);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_addnote(): adds notes to a database

  int com_addnote  0 if successful, 1 on error

  char *arg the name of the file containing the notes

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_addnote (char* arg)
{
  return addnote(arg, 0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_updatenote(): updates notes in a database

  int com_updatenote  0 if successful, 1 on error

  char *arg the name of the file containing the notes

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_updatenote (char* arg)
{
  return addnote(arg, 1);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  addnote(): adds or replaces notes to a database

  static int com_addnote  0 if successful, 1 on error

  char *arg the name(s) of the file(s) containing the notes

  int update_ref if 1, the notes will be replaced in the db;
            if 0, the notes will be added

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int addnote (char* arg, int update_note)
{
  int n_sockfd; /* file descriptor of the socket */
  char srv_inbuffer[COMMAND_INBUF_LEN] = "";
  char set_owner[32] = "";
  char db[_POSIX_PATH_MAX+1] = ""; /* the database name */
  char outfile[_POSIX_PATH_MAX+1] = ""; /* output filename */
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  char my_fromencoding[PREFS_BUF_LEN] = "";
  char *outbuffer = NULL;
  char *inbuffer = NULL;
  char *ris_set_buffer = NULL;
  char **inargv; /* tokens of the argument */
  char *newarg = NULL;
  char *outpipe = NULL;
  int numbyte;
  int numcycles; /* number of cycles reading input */
  int n_read_done = 0;
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int n_opt;
  int n_cmdlinerror = 0;
  int result;
  int n_personal = 0;
  int n_file_append = 0; /* indicates -O switch */
  int n_file_open = 0; /* indicates -o switch */
  int n_pipe = 0; /* indicates -c switch */
  int n_keep_id = 0; /* indicates -k switch */
  int n_send_result;
  int n_curr_trailing_z = 0;
  int n_last_trailing_z = 0;
  int k;
  int cs_status;
  size_t byte_written = 0;
  FILE *pagerfp;
  FILE *infp = NULL;
  FILE* errstream;
  struct lilimem sentinel;

  errstream = (n_cgi) ? stdout : stderr;

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

  strcpy(db, current_db); /* use default db if set */
  /* 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)+9));
  if (newarg == NULL) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

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

  strcpy(newarg, "addnote ");
  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;
    
  if (update_note) {

    while ((n_opt = getopt(inargc, inargv, "aA:c:C:d:e:E:f:F:g:G:hi:kl:L:o:O:p:Pqr:R:s:S:t:T:u: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 'd':
	/*  	printf("-d %s\n", optarg); */
	strncpy(db, optarg, _POSIX_PATH_MAX-1); /* override preset db */
	db[_POSIX_PATH_MAX-1] = '\0';
	break;
      case 'E':
	strncpy(my_fromencoding, optarg, PREFS_BUF_LEN);
	my_fromencoding[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'f':
	/*        printf("-f %s\n", optarg); */
	if (!strcmp(optarg, "stdin")) {
	  n_read_stdin = 1;
	}
	break;
      case 'h':
	delete_all_lilimem(&sentinel);
	fprintf(errstream, "Replaces the specified notes in the database\nSyntax: updatenote [-c command] [-d database] [-E encoding] [-h] [-o outfile] [-O outfile] [-P] [-t type] [-U username] {file|-f infile}\nOptions: -c command   pipe the output through command\n         -d database  specify the database to work with\n         -E encoding  specify the input character encoding\n         -f infile    Read the names of the files with the notes from file infile\n         -g deffile    read global fields from file deffile\n         -h           prints this mini-help\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         -U username  specify a different username than the current user\n         All other arguments are interpreted as filenames.\n");
	return 0;
	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 'U':
	/*  	printf("-U %s\n", optarg); */
	strcpy(set_owner, optarg);
	break;
	/* now all the options that main has already taken care of */
      case 'a':
      case 'A':
      case 'C': /* fall through - nothing to do */
      case 'e':
      case 'F':
      case 'g':
      case 'G':
      case 'i':
      case 'k':
      case 'l':
      case 'L':
      case 'p':
      case 'q':
      case 'P':
      case 'r':
      case 'R':
      case 'S':
      case 's':
      case 't':
      case 'T':
      case 'u':
      case 'v':
      case 'V':
      case 'w':
	break;
      case ':':
	fprintf(errstream, "missing option\n");
	n_cmdlinerror = 1;
	break;
      case '?':
	fprintf(errstream, "unknown option\n");
	n_cmdlinerror = 1;
	break;
      }
    }
  }
  else {
    
    while ((n_opt = getopt(inargc, inargv, "aA:c:C:d:e:E:f:F:g:G:hi:kl:L:o:O:p:Pqr:R:s:S:t:T:u: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 'd':
	/*  	printf("-d %s\n", optarg); */
	strcpy(db, optarg); /* override preset db */
	break;
      case 'E':
	strncpy(my_fromencoding, optarg, PREFS_BUF_LEN);
	my_fromencoding[PREFS_BUF_LEN-1] = '\0';
	break;
      case 'f':
	/*        printf("-f %s\n", optarg); */
	if (!strcmp(optarg, "stdin")) {
	  n_read_stdin = 1;
	}
	break;
      case 'h':
	delete_all_lilimem(&sentinel);
	fprintf(errstream, "Adds the specified notes to the database\nSyntax: addnote [-c command] [-d database] [-E encoding] [-g deffile] [-h] [-k] [-o outfile] [-O outfile] [-U username] {file|-f infile}\nOptions: -c command   pipe the output through command\n         -d database  specify the database to work with\n         -E encoding  specify the input character encoding\n         -f infile    Read the names of the files with the  notes from file infile\n         -g deffile    read global fields from file deffile\n         -h           prints this mini-help\n         -k           keep note ID\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         -U username  specify a different username than the current user\n         All other arguments are interpreted as filenames.\n");
	return 0;
	break;
      case 'k':
	n_keep_id = 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_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 'U':
	/*  	printf("-U %s\n", optarg); */
	strcpy(set_owner, optarg);
	break;
	/* now all the options that main has already taken care of */
      case 'a':
      case 'A':
      case 'C': /* fall through - nothing to do */
      case 'e':
      case 'F':
      case 'g':
      case 'G':
      case 'i':
      case 'l':
      case 'L':
      case 'p':
      case 'P':
      case 'q':
      case 'r':
      case 'R':
      case 'S':
      case 's':
      case 't':
      case 'T':
      case 'u':
      case 'v':
      case 'V':
      case 'w':
	break;
      case ':':
	fprintf(errstream, "missing option\n");
	n_cmdlinerror = 1;
	break;
      case '?':
	fprintf(errstream, "unknown option\n");
	n_cmdlinerror = 1;
	break;
      }
    }
  }

	    
  /* get arguments */
/*    { */
/*      int i; */
/*      for (i = optind; i < inargc; i++) { */
/*        printf("1argument %s<<\n", inargv[i]); */
/*      } */
/*    } */

  if (!*db) {
    fprintf(errstream, "Don't know which database to use. Select one with selectdb or use the -d switch with addnote.\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (n_cmdlinerror) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  /* try to connect to our server */
  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)) {
    delete_all_lilimem(&sentinel);
    close(n_sockfd);
    return 1;
  }

  /* get us some memory, will be realloc'd by read_ris_set() as needed */
  ris_set_buffer = malloc(1);
  if (insert_lilimem(&sentinel, (void**)&ris_set_buffer, NULL)) {
    send_status(n_sockfd, 112, TERM_NO);
    delete_all_lilimem(&sentinel);
    close(n_sockfd);
    return 1;
  }


  /* and more memory for client/server communication */
  inbuffer = malloc(COMMAND_INBUF_LEN);
  if (insert_lilimem(&sentinel, (void**)&inbuffer, NULL)) {
    send_status(n_sockfd, 112, TERM_NO);
    delete_all_lilimem(&sentinel);
    close(n_sockfd);
    return 1;
  }

  outbuffer = malloc(COMMAND_INBUF_LEN);
  if (insert_lilimem(&sentinel, (void**)&outbuffer, NULL)) {
    send_status(n_sockfd, 112, TERM_NO);
    delete_all_lilimem(&sentinel);
    close(n_sockfd);
    return 1;
  }

  if (update_note) {
    strcpy(outbuffer, "updatenote ");
  }
  else {
    strcpy(outbuffer, "addnote ");
  }

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

  if (*set_owner) {
    strcat(outbuffer, " -U ");
    strcat(outbuffer, set_owner);
  }

  if (*my_fromencoding) {
    strcat(outbuffer, " -E ");
    strcat(outbuffer, my_fromencoding);
  }

  if (n_personal) {
    strcat(outbuffer, " -p");
  }

  if (n_keep_id) {
    strcat(outbuffer, " -k");
  }

/*   if (*outtype) { */
/*     strcat(outbuffer, " -t "); */
/*     strcat(outbuffer, outtype); */
/*   } */
/*    free(newarg); */
/*    newarg = NULL; */

  LOG_PRINT(LOG_DEBUG, outbuffer);
  send_status(n_sockfd, 0, TERM_NO);
  numbyte = tiwrite(n_sockfd, outbuffer, TERM_YES);
  /*  printf("%s\n", outbuffer); */

  if (numbyte == -1) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "could not write to refdbd. Stop\n");
    delete_all_lilimem(&sentinel);
    close(n_sockfd);
    return 1;
  }
  
  cs_status = read_status(n_sockfd);

  /* todo: is a 701 possible here? */
  if (cs_status == 701) {
    /* character conversion could not be initialized */
    fprintf(errstream, "%s\n", get_status_msg(701));
  }
  else if (cs_status) {
    cgi_header(CGI_PLAIN);
    delete_all_lilimem(&sentinel);
    LOG_PRINT(LOG_WARNING, get_status_msg(cs_status));
    close(n_sockfd);
    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;
  }
/*    printf("%d\n", numcycles); */
  for (k = 0; k < numcycles; k++) {
    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 4 of communication protocol */
    n_send_result = send_xml_data(infp, pagerfp, errstream, n_sockfd, &byte_written);

    if (n_send_result) {
      if (!n_read_stdin) {
	fclose(infp);
      }
      if (n_file_open || n_file_append) {
	close_outfile(pagerfp);
      }
      else {
	closepager(pagerfp);
      }
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      n_broken_pipe = 0;
      return 1;
    }
  }

  /* ------------------------------------------------------------ */
  /* PHASE 5 */
  /*  signal server that we're done */
  
  send_status(n_sockfd, 402, TERM_NO);

  /* check server status, should be "chunk added successfully" */
  if ((cs_status = read_status(n_sockfd)) != 403) {
    if (cs_status == 400) {
      int num_trailz;
      int n_read_done = 0;
      size_t byte_written_to_inbuffer;

      /* 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 */

  /* read data in loop */
  do {
    numbyte = tread(n_sockfd, inbuffer, OUTBUF_LEN);
    if (numbyte == -1) {
      fprintf(stderr, "%s", get_status_msg(109));
      if (n_file_open || n_file_append) {
	close_outfile(pagerfp);
      }
      else {
	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) {
	/* terminator is complete */
	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) {
      /* terminator is complete including the previous cycle */
      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;
    }
  } while (!n_read_done);


  /* read summary */
  send_status(n_sockfd, 0, TERM_NO);
  cs_status = read_status(n_sockfd);

  if (cs_status != 0 /* success */
      && cs_status != 803) { /* partial success */
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "%s", get_status_msg(cs_status));
    return 1;
  }

  numbyte = tread(n_sockfd, inbuffer, OUTBUF_LEN);
  if (numbyte == -1) {
    fprintf(errstream, "could not read from refdbd. Stop\n");
    n_broken_pipe = 0;
    return 1;
  }
      
  /* send back confirmation to the server */
  send_status(n_sockfd, 0, TERM_NO);

  /* reset */
  n_broken_pipe = 0;

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

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

cleanup:
  /*    printf("%s\n", inbuffer); */
  delete_all_lilimem(&sentinel);
  close(n_sockfd);
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_deletenote(): delete notes

  int com_deletenotes 0 if successful, 1 if error 

  char *arg the spec of the notes which are to be deleted

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_deletenote (char* arg)
{
  char *new_outbuffer = NULL;
  char cmd_buffer[OUTBUF_LEN] = "";
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char **inargv = NULL; /* tokens of the argument */
  char *newarg = NULL;
  char* infile = NULL;
  char db[DBNAME_LENGTH] = "";
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  int numbyte = 0;
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int n_cmdlinerror = 0;
  int result;
  int n_opt;
  int i;
  int n_read_file = 0;
  int cs_status;
  size_t outbuf_len;
  FILE *infilefp;
  FILE* errstream;
  struct simplelistvals slvals;
  struct lilimem sentinel;

  errstream = (n_cgi) ? stdout : stderr;

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

  /* get us some buffer for output */
  outbuf_len = 128; /* something to start with */
  slvals.outbuffer = malloc(outbuf_len); 
  if (slvals.outbuffer == NULL) {
    return 1;
  }
  slvals.outbuffer[0] = '\0';

  slvals.n_file_open = 0;
  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = NULL;
  slvals.outpipe = NULL;

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

  strcpy(db, current_db); /* use default db if set */

  strcpy(cmd_buffer, "deletenote ");

  /* 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) {
    delete_all_lilimem(&sentinel);
    return 1;
  }
  
  if (insert_lilimem(&sentinel, (void**)&inargv, NULL)) {
    delete_all_lilimem(&sentinel);
    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, "deletenote ");
  strcat(newarg, arg);

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


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

  /* get options */
  optind = 0;

  while ((n_opt = getopt(inargc, inargv, "aA:c:C:d:e:E:f:F:g:G:hi:kl:L:o:O:p:Pqr:R:s:S:t:T:u: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 'd':
      /*        printf("-d %s\n", optarg); */
      strcpy(db, optarg); /* override preset db */
      break;
    case 'f':
      /*        printf("-f %s\n", optarg); */
      if (!strcmp(optarg, "stdin")) {
	n_read_stdin = 1;
      }
      else {
	infile = canonicalize_path(optarg);
	if (insert_lilimem(&sentinel, (void**)&infile, NULL)) {
	  delete_all_lilimem(&sentinel);
	  return 1;
	}
	n_read_file = 1;
      }
      break;
    case 'h':
      printf("Deletes the specified notes from the database\nSyntax: deletenote [-c command] [-d database] [-h] [-o outfile] [-O outfile] {ID|-f infile}\nOptions: -c command   pipe the output through command\n         -d database  specify the database to work with\n         -f infile    Read the reference IDs from file infile\n         -h           prints this mini-help\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         All other arguments are interpreted as IDs to delete.\n");
      delete_all_lilimem(&sentinel);
      return 0;
      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;
      /* now all the options that main has already taken care of */
    case 'a':
    case 'A':
    case 'C': /* fall through - nothing to do */
    case 'e':
    case 'E':
    case 'F':
    case 'g':
    case 'G':
    case 'i':
    case 'k':
    case 'l':
    case 'L':
    case 'p':
    case 'P':
    case 'q':
    case 'r':
    case 'R':
    case 's':
    case 'S':
    case 't':
    case 'T':
    case 'u':
    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 (!*db) {
    fprintf(errstream, "Don't know which database to use. Select one with selectdb or use the -d switch with deletenote.\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (n_cmdlinerror) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  /* try to connect to server */
  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;
  }

  /* prepare data */
  if (n_read_file || n_read_stdin || optind == inargc) {
    if (n_read_file) {
      if ((infilefp = fopen(infile, "rb")) == NULL) {
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	return 1;
      }
    }
    else {
      infilefp = stdin;
      n_read_stdin = 1; /* not set if optind == inargc */
    }
    result = add_id_from_ris(infilefp, &(slvals.outbuffer), &outbuf_len);

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

    if (result) {
      delete_all_lilimem(&sentinel);
      close(slvals.n_sockfd);
      return 1;
    }
  }

  for (i = optind; i < inargc; i++) {
    if ((new_outbuffer = mstrcat(slvals.outbuffer, inargv[i], &outbuf_len, 0)) == NULL) {
      delete_all_lilimem(&sentinel);
      close(slvals.n_sockfd);
      return 1;
    }
    else {
      slvals.outbuffer = new_outbuffer;
    }
    if ((new_outbuffer = mstrcat(slvals.outbuffer, " ", &outbuf_len, 0)) == NULL) {
      delete_all_lilimem(&sentinel);
      close(slvals.n_sockfd);
      return 1;
    }
    else {
      slvals.outbuffer = new_outbuffer;
    }
  }

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

  sprintf(cmd_buffer+strlen(cmd_buffer), " %d", strlen(slvals.outbuffer)+TERM_LEN); 

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

  /* send command to application server */
  send_status(slvals.n_sockfd, 0, TERM_NO);
  numbyte = tiwrite(slvals.n_sockfd, cmd_buffer, TERM_YES);
  LOG_PRINT(LOG_DEBUG, cmd_buffer);
  if (numbyte == -1) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "%s\n", get_status_msg(110));
    close(slvals.n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }

  numbyte = 0;

  if ((cs_status = read_status(slvals.n_sockfd)) != 0) {
    fprintf(errstream, "%s\n", get_status_msg(cs_status));
    close(slvals.n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }

  getsimplelist(&slvals, 1);

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

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_getnote(): view extended notes

  int com_getnote 0 if successful, 1 if error 

  char *arg the spec of the notes which are to be viewed

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_getnote (char* arg)
{
  return getnote(arg, 1);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_countnote(): count notes

  int com_countnote 0 if successful, 1 if error 

  char *arg the spec of the notes which are to be viewed

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_countnote (char* arg)
{
  return getnote(arg, 0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  getnote(): view notes

  int getnote 0 if successful, 1 if error 

  char *arg the spec of the note which are to be viewed

  int send_data if set to 1, send back references (getref)
                if set to 0, count references (countref)

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int getnote (char* arg, int send_data)
{
  char cmd_buffer[OUTBUF_LEN] = "";
  char* infile;
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char format_string[MAX_FMT_LEN] = "";
  char sort_string[MAX_FMT_LEN] = "";
  char db[_POSIX_PATH_MAX+1] = "";
  char pdf_root[_POSIX_PATH_MAX] = "";
  char type_string[10] = "";
  char my_toencoding[PREFS_BUF_LEN] = "";
  char limit[PREFS_BUF_LEN] = "";
  char listname[PREFS_BUF_LEN] = "";
  char **inargv; /* tokens of the argument */
  char *newarg;
  char *read_result;
  char *new_outbuffer;
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int result;
  int i;
  int n_opt;
  int n_done;
  int n_read_file = 0; /* indicates -f switch */
  int n_cmdlinerror = 0;
  int numbyte;
  int cs_status;
  size_t byte_written = 0;
  size_t outbuf_len;
  FILE *infilefp;
  FILE* errstream;
  FILE *pagerfp; /* ptr to file */
  struct lilimem sentinel;
  struct simplelistvals slvals;

  errstream = (n_cgi) ? stdout : stderr;

   /* get us some buffer for output */
  outbuf_len = 256; /* something to start with */
  slvals.outbuffer = malloc(outbuf_len); 
  if (slvals.outbuffer == NULL
      || insert_lilimem(&sentinel, (void**)&(slvals.outbuffer), NULL)) {
    free(slvals.outbuffer);
    return 1;
  }
  slvals.outbuffer[0] = '\0';

  if (!send_data) {
    strcpy(cmd_buffer, "countnote ");
  }
  else {
    strcpy(cmd_buffer, "getnote ");
  }

  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';

  if (*pdfroot) {
    strcpy(pdf_root, pdfroot);
  }

  strcpy(db, current_db); /* use default db if set */
  strcpy(my_toencoding, toencoding); /* use default encoding if set */

  /* 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)+9));
  if (newarg == NULL) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (insert_lilimem(&sentinel, (void**)&newarg, NULL)) {
    delete_all_lilimem(&sentinel);
    return 1;
  }
  
  strcpy(newarg, "getnote ");
  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, "aA:b:c:C:d:e:E:f:F:g:G:hi:kl:L:n:N:o:O:p:qr:R:s:S:t:T:u:U:vVw:")) != -1) {
    switch(n_opt) {
    case 'b':
      strncpy(listname, optarg, PREFS_BUF_LEN);
      listname[PREFS_BUF_LEN-1] = '\0';
      break;
    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 'd':
      /*        printf("-d %s\n", optarg); */
      strncpy(db, optarg, _POSIX_PATH_MAX - 1); /* override preset db */
      db[_POSIX_PATH_MAX-1] = '\0';  /* terminate in case string got truncated */
      break;
    case 'E':
      strncpy(my_toencoding, optarg, PREFS_BUF_LEN);
      my_toencoding[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'f':
      /*        printf("-f %s\n", optarg); */
      if (!strcmp(optarg, "stdin")) {
	n_read_stdin = 1;
      }
      else {
	infile = canonicalize_path(optarg);
	if (insert_lilimem(&sentinel, (void**)&infile, NULL)) {
	  delete_all_lilimem(&sentinel);
	  return 1;
	}
	n_read_file = 1;
      }
      break;
    case 'h':
      if (!send_data) {
	printf("Displays the result of a database search for notes.\nSyntax: getnote  [-b listname] [-c command] [-d database] [-h] [-n namespace] [-N limit:offset] {search-string|-f infile}\nSearch-string: {:XY:{<|<=|=|~|!=|<>|!~|>|>=}{string|regexp}} [AND|OR|AND NOT] [...]\nwhere XY specifies the field to search in\nOptions: -b listname  limit search to the specified personal reference list\n          -c command   pipe the output through command\n         -d database  specify the database to work with\n         -h           prints this mini-help\n         -n namespace optional namespace for XML output\n         -N limit:offset specify range of datasets\n         -f infile    use the saved search line in file infile\n         All other arguments are interpreted as the search string.\n");
      }
      else {
	printf("Displays the result of a database search for notes.\nSyntax: getnote  [-b listname] [-c command] [-d database] [-E encoding] [-h] [-n namespace] [-N limit:offset] [-o outfile] [-O outfile][-P] [-R pdfroot] [-s format] [-S tag] [-t output-format] {search-string|-f infile}\nSearch-string: {:XY:{<|<=|=|~|!=|<>|!~|>|>=}{string|regexp}} [AND|OR|AND NOT] [...]\nwhere XY specifies the field to search in\nOptions:  -b listname  limit search to the specified personal reference list\n         -c command   pipe the output through command\n         -d database  specify the database to work with\n         -E encoding  specify the input character encoding\n         -h           prints this mini-help\n         -n namespace optional namespace for XML output\n         -N limit:offset specify range of datasets\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n         -P           limit search to personal interest list\n         -R           use pdfroot as root for path of pdf files\n         -s format    specify fields for screen or style for DocBook output\n         -S tag       sort output by tag ID (default) or PY\n         -t output-format display as format scrn, html, xhtml, or xnote\n         -f infile    use the saved search line in file infile\n         All other arguments are interpreted as the search string.\n");
      }
      delete_all_lilimem(&sentinel);
      return 0;
      break;
    case 'N':
      strncpy(limit, optarg, PREFS_BUF_LEN);
      limit[PREFS_BUF_LEN-1] = '\0';
      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 'R':
      strcpy(pdf_root, optarg);
      break;
    case 's':
      /*        printf("-s %s\n", optarg); */
      strncpy(format_string, optarg, MAX_FMT_LEN - 1);
      format_string[MAX_FMT_LEN-1] = '\0';  /* terminate in case string got truncated */
      break;
    case 'S':
      /*        printf("-S %s\n", optarg); */
      strncpy(sort_string, optarg, MAX_FMT_LEN - 1);
      sort_string[MAX_FMT_LEN-1] = '\0';  /* terminate in case string got truncated */
      break;
    case 't':
      /*        printf("-t %s\n", optarg); */
      strncpy(type_string, optarg, 9);
      type_string[9] = '\0';  /* terminate in case string got truncated */
      break;
      /* now all the options that main has already taken care of */
    case 'a':
    case 'A':
    case 'C': /* fall through */
    case 'e':
    case 'F':
    case 'g':
    case 'G':
    case 'i':
    case 'k':
    case 'l':
    case 'L':
    case 'p':
    case 'q':
    case 'r':
    case 'T':
    case 'u':
    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 (!*db) {
    fprintf(errstream, "Don't know which database to use. Select one with selectdb or use the -d switch with getnote\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (n_cmdlinerror) {
    delete_all_lilimem(&sentinel);
    return 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)) {
    delete_all_lilimem(&sentinel);
    close(slvals.n_sockfd);
    return 1;
  }


  if (n_read_file || n_read_stdin || optind == inargc) {
    char filebuffer[COMMAND_INBUF_LEN];

    if (n_read_file) {
      infilefp = fopen(infile, "rb");
      if (infilefp == NULL) {
	send_status(slvals.n_sockfd, 112, TERM_NO);
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	return 1;
      }
    }
    else {
      infilefp = stdin; /* stdin is already open */
      n_read_stdin = 1; /* not set if optind == inargc */
    }

    while ((read_result = fgets(filebuffer, COMMAND_INBUF_LEN, infilefp)) != NULL) {
      if ((new_outbuffer = mstrcat(slvals.outbuffer, filebuffer, &outbuf_len, 0)) == NULL) {
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	if (!n_read_stdin) { /* don't close stdin */
	  fclose(infilefp);
	}
	return 1;
      }
      else {
	slvals.outbuffer = new_outbuffer;
      }
    }

    if (ferror(infilefp)) {
      send_status(slvals.n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(slvals.n_sockfd);
      return 1;
    }
    if (!n_read_stdin) { /* don't close stdin */
      fclose(infilefp);
    }
  }
  else {
    if (optind < inargc) {
      int open_quote = 0;

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

	if (open_quote) {
	  if ((new_outbuffer = mstrcat(slvals.outbuffer, "\'", &outbuf_len, 0)) == NULL) {
	    delete_all_lilimem(&sentinel);
	    close(slvals.n_sockfd);
	    return 1;
	  }
	  else {
	    slvals.outbuffer = new_outbuffer;
	  }

	  open_quote--;
	}
	/* the tokenizer returns a quoted item as a separate token
	   even if there is no space between e.g. a '=' and the
	   quoted item. To rectify this, check whether the previous
	   item ended with a '=' or a '~' */
	if (slvals.outbuffer[strlen(slvals.outbuffer)-1] != '='
	    && slvals.outbuffer[strlen(slvals.outbuffer)-1] != '~') {
	  if ((new_outbuffer = mstrcat(slvals.outbuffer, " ", &outbuf_len, 0)) == NULL) {
	    delete_all_lilimem(&sentinel);
	    close(slvals.n_sockfd);
	    return 1;
	  }
	  else {
	    slvals.outbuffer = new_outbuffer;
	  }
	}
	else {
	  /* insert a quote after the '='. This will put back the
	     quote only for items that actually were quoted */
	  if ((new_outbuffer = mstrcat(slvals.outbuffer, "\'", &outbuf_len, 0)) == NULL) {
	    delete_all_lilimem(&sentinel);
	    close(slvals.n_sockfd);
	    return 1;
	  }
	  else {
	    slvals.outbuffer = new_outbuffer;
	  }

	  open_quote++;
	}
      }
      
      slvals.outbuffer[strlen(slvals.outbuffer)-1] = '\0'; /* remove trailing space */
    }
  }

  /* make sure there's no newlines in the string; reuse read_result */
  for (read_result = slvals.outbuffer; *read_result; read_result++) {
    if (*read_result == '\n' || *read_result == '\r') {
      *read_result = ' ';
    }
  }

  /* assemble command string for refdbd */
  strcat(cmd_buffer, " -u ");
  strcat(cmd_buffer, username);
  if (*passwd) {
    strcat(cmd_buffer, " -w ");
    strcat(cmd_buffer, scrambled_passwd);
  }
  strcat(cmd_buffer, " -d ");
  strcat(cmd_buffer, db);

  if (*format_string || *default_fields) {
    strcat(cmd_buffer, " -s \"");
    if (*default_fields) {
      strcat(cmd_buffer, default_fields);
    }
    else {
      strcat(cmd_buffer, format_string);
    }
    strcat(cmd_buffer, "\"");
  }

  if (*type_string) {
    strcat(cmd_buffer, " -t \"");
    strcat(cmd_buffer, type_string);
    strcat(cmd_buffer, "\"");
  }

  if (*sort_string) {
    strcat(cmd_buffer, " -S \"");
    strcat(cmd_buffer, sort_string);
    strcat(cmd_buffer, "\"");
  }

  if (*limit) {
    strcat(cmd_buffer, " -N \"");
    strcat(cmd_buffer, limit);
    strcat(cmd_buffer, "\"");
  }

  if (*pdf_root) {
    strcat(cmd_buffer, " -R ");
    strcat(cmd_buffer, pdf_root);
  }
  
  if (*my_toencoding) {
    strcat(cmd_buffer, " -E ");
    strcat(cmd_buffer, my_toencoding);
  }
  
  if (*css_url) {
    strcat(cmd_buffer, " -G ");
    strcat(cmd_buffer, css_url);
  }

  if (*listname) {
    strcat(cmd_buffer, " -b ");
    strcat(cmd_buffer, listname);
  }

  sprintf(cmd_buffer+strlen(cmd_buffer), " %d", strlen(slvals.outbuffer)+TERM_LEN); 

  LOG_PRINT(LOG_DEBUG, cmd_buffer);

  errstream = (n_cgi) ? stdout : stderr;

  send_status(slvals.n_sockfd, 0, TERM_NO);

  numbyte = tiwrite(slvals.n_sockfd, cmd_buffer, TERM_YES);
  if (numbyte == -1) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "could not write to refdbd. Stop\n");
    return 1;
  }

  numbyte = 0;

  if ((cs_status = read_status(slvals.n_sockfd)) != 0) {
    fprintf(errstream, "%s\n", get_status_msg(cs_status));
    close(slvals.n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }

  send_status(slvals.n_sockfd, 0, TERM_NO);
  numbyte = tiwrite(slvals.n_sockfd, slvals.outbuffer, TERM_YES);
  LOG_PRINT(LOG_DEBUG, cmd_buffer);
  if (numbyte == -1) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "could not write to refdbd. Stop\n");
    close(slvals.n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }

  /* openpager and open_outfile are guaranteed to return a valid file/pipe - 
     and be it stdout */
  if (slvals.n_file_open) {
    pagerfp = open_outfile(slvals.outfile, 0);
  }
  else if (slvals.n_file_append) {
    pagerfp = open_outfile(slvals.outfile, 1);
  }
  else if (slvals.n_pipe) {
    pagerfp = openpager(slvals.outpipe);
  }
  else {
    pagerfp = openpager(the_pager);
  }

  n_done = 0;

  do { /* loop until we have all requested datasets */
    int n_error;

    cs_status = read_status(slvals.n_sockfd);

    if (cs_status != 402 /* last dataset */
	&& cs_status != 404) { /* finished dataset */
      cgi_header(CGI_PLAIN);
      fprintf(errstream, "%s\n", get_status_msg(cs_status));
      if (slvals.n_file_open || slvals.n_file_append) {
	close_outfile(pagerfp);
      }
      else {
	closepager(pagerfp);
      }
      n_broken_pipe = 0;
      return 1;
    }
  
    if (cs_status == 402) {
      n_done++;
    }

    byte_written += read_terminated_string(&slvals, pagerfp, &n_error);

    if (n_error) {
      delete_all_lilimem(&sentinel);
      if (slvals.n_file_open || slvals.n_file_append) {
	close_outfile(pagerfp);
      }
      else {
	closepager(pagerfp);
      }
      n_broken_pipe = 0;
      return 1;
    }
  } while (!n_done);

  /* read summary */
/*   send_status(slvals.n_sockfd, 0, TERM_NO); */
  cs_status = read_status(slvals.n_sockfd);
	
  if (cs_status != 0 /* success */
      && cs_status != 803) { /* partial success */
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "%s\n", get_status_msg(cs_status));
    return 1;
  }
	
  numbyte = tread(slvals.n_sockfd, slvals.inbuffer, OUTBUF_LEN);
  if (numbyte == -1) {
    fprintf(stderr, "%s\n", get_status_msg(109));
    n_broken_pipe = 0;
    return 1;
  }
	
  send_status(slvals.n_sockfd, 0, TERM_NO);
      
  if (slvals.n_file_open || slvals.n_file_append) {
    close_outfile(pagerfp);
    fprintf(errstream, "%d byte written to %s\n", byte_written, slvals.outfile);
  }
  else {
    closepager(pagerfp);
  }

  close(slvals.n_sockfd);

  fprintf(errstream, "%s", slvals.inbuffer);

  delete_all_lilimem(&sentinel);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_addlink(): add links from notes to other objects

  int com_addlink 0 if successful, 1 if error 

  char *arg the spec of the note which are to be viewed

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_addlink (char* arg) {
  return addlink(0, arg);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_deletelink(): remove links from notes to other objects

  int com_deletelink 0 if successful, 1 if error 

  char *arg the spec of the note which are to be viewed

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_deletelink (char* arg) {
  return addlink(1, arg);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  addlink(): add or remove links from notes to other objects

  int addlink 0 if successful, 1 if error 

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

  char *arg the spec of the note which are to be viewed

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int addlink (int n_remove, char* arg) {
  char outbuffer[COMMAND_INBUF_LEN]; /* holds the command for the server */
  char* infile;
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char db[_POSIX_PATH_MAX+1] = "";
  char **inargv; /* tokens of the argument */
  char *newarg;
  char *read_result;
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int result;
  int i;
  int n_opt;
  int n_read_file = 0; /* indicates -f switch */
  int n_cmdlinerror = 0;
  FILE *infilefp;
  FILE* errstream;
  struct lilimem sentinel;
  struct simplelistvals slvals;

  errstream = (n_cgi) ? stdout : stderr;

  slvals.outbuffer = outbuffer;

  strcpy(slvals.outbuffer, "addlink ");
  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';

  strcpy(db, current_db); /* use default db if set */

  /* 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)+8));
  if (newarg == NULL) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (insert_lilimem(&sentinel, (void**)&newarg, NULL)) {
    delete_all_lilimem(&sentinel);
    return 1;
  }
  
  strcpy(newarg, "addlink ");
  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, "aA:c:C:d:e:E:f:F:g:G:hi:kl:L:o:O:p:Pqr:R:s:S:t:T:u: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 'd':
      /*        printf("-d %s\n", optarg); */
      strncpy(db, optarg, _POSIX_PATH_MAX - 1); /* override preset db */
      db[_POSIX_PATH_MAX-1] = '\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, NULL)) { */
/* 	  delete_all_lilimem(&sentinel); */
/* 	  return 1; */
/* 	} */
/* 	n_read_file = 1; */
/*       } */
/*       break; */
    case 'h':
      if (n_remove) {
	printf("Unlinks an existing note from one or more references, keywords, periodicals, or authors.\nSyntax: deletelink  [-c command] [-d database] [-h] [-o outfile] [-O outfile] :NID:|:NCK: :XY:=value [:XY:=value1...]\nNID or NCK specify a note by its ID or key, respectively\nXY is one of ID, CK (reference by ID or citekey), KW (keyword), JO|JF|J1|J2 (periodical abbreviated|full|user1|user2), AU (author name)\nOptions: -c command   pipe the output through command\n         -d database  specify the database to work with\n         -h           prints this mini-help\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n");
      }
      else {
	printf("Links an existing note to one or more references, keywords, periodicals, or authors.\nSyntax: addlink  [-c command] [-d database] [-h] [-o outfile] [-O outfile] :NID:|:NCK: :XY:=value [:XY:=value1...]\nNID or NCK specify a note by its ID or key, respectively\nXY is one of ID, CK (reference by ID or citekey), KW (keyword), JO|JF|J1|J2 (periodical abbreviated|full|user1|user2), AU (author name)\nOptions: -c command   pipe the output through command\n         -d database  specify the database to work with\n         -h           prints this mini-help\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n");
      }
      delete_all_lilimem(&sentinel);
      return 0;
      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;
      /* now all the options that main has already taken care of */
    case 'a':
    case 'A':
    case 'C': /* fall through */
    case 'e':
    case 'E':
    case 'f':
    case 'F':
    case 'g':
    case 'G':
    case 'i':
    case 'k':
    case 'l':
    case 'L':
    case 'p':
    case 'P':
    case 'q':
    case 'R':
    case 's':
    case 'S':
    case 't':
    case 'T':
    case 'u':
    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 (!*db) {
    fprintf(errstream, "Don't know which database to use. Select one with selectdb or use the -d switch with addlink\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (n_cmdlinerror) {
    delete_all_lilimem(&sentinel);
    return 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)) {
    delete_all_lilimem(&sentinel);
    close(slvals.n_sockfd);
    return 1;
  }


  if (n_read_file || n_read_stdin) {
    if (!n_read_stdin) {
      infilefp = fopen(infile, "rb");
      if (infilefp == NULL) {
	send_status(slvals.n_sockfd, 112, TERM_NO);
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	return 1;
      }
    }
    else {
      infilefp = stdin; /* stdin is already open */
    }

    read_result = fgets(slvals.outbuffer+strlen(slvals.outbuffer), COMMAND_INBUF_LEN-strlen(slvals.outbuffer), infilefp);
    if (!n_read_stdin) { /* don't close stdin */
      fclose(infilefp);
    }
    if (read_result == NULL) {
      send_status(slvals.n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(slvals.n_sockfd);
      return 1;
    }
  }
  else {
    if (optind < inargc) {
      strcat(slvals.outbuffer, "\"");
      /*      printf("%d\n", optind); */
      for (i = optind; i < inargc; i++) {
/* 	printf("inargv[%d]: %s<<\n", i, inargv[i]); */
/* 	strcat(slvals.outbuffer, inargv[i]); */
	/* the tokenizer returns a quoted item as a separate token
	   even if there is no space between e.g. a '=' and the
	   quoted item. To rectify this, check whether the previous
	   item ended with a '=' */
	if (slvals.outbuffer[strlen(slvals.outbuffer)-1] != '=') {
	  strcat(slvals.outbuffer, inargv[i]);
	  if (slvals.outbuffer[strlen(slvals.outbuffer)-1] != '=') {
	    strcat(slvals.outbuffer, " ");
	  }
	}
	else {
	  /* quote item */
	  strcat(slvals.outbuffer, "\'");

	  /* in order to allow e.g. single quotes in journal names we
	     must escape the string properly. It will be unescaped on
	     the server side */
	  escape_chars(slvals.outbuffer + strlen(slvals.outbuffer), inargv[i], strlen(inargv[i]), "'\"");
	  strcat(slvals.outbuffer, "\' ");
	}
      }
      strcpy(&slvals.outbuffer[strlen(slvals.outbuffer)-1], "\""); /* remove trailing space */
    }
  }

  /* ToDo: do we need this here? */
  /* make sure there's no newlines in the string; reuse read_result */
  for (read_result = slvals.outbuffer; *read_result; read_result++) {
    if (*read_result == '\n' || *read_result == '\r') {
      *read_result = ' ';
    }
  }

  /* assemble command string for refdbd */
  strcat(slvals.outbuffer, " -u ");
  strcat(slvals.outbuffer, username);
  if (*passwd) {
    strcat(slvals.outbuffer, " -w ");
    strcat(slvals.outbuffer, scrambled_passwd);
  }
  strcat(slvals.outbuffer, " -d ");
  strcat(slvals.outbuffer, db);

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

  LOG_PRINT(LOG_DEBUG, slvals.outbuffer);
  getsimplelist(&slvals, 1);

/*   iwrite(slvals.n_sockfd, "POS", 4); */

  close(slvals.n_sockfd);

  delete_all_lilimem(&sentinel);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_updatejo(): update the journal name synonyms

  int com_updatejo 0 if successful, 1 if error 

  char *arg the spec of the note which are to be viewed

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_updatejo (char* arg) {
  char outbuffer[COMMAND_INBUF_LEN]; /* holds the command for the server */
  char* infile;
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char db[_POSIX_PATH_MAX+1] = "";
  char **inargv; /* tokens of the argument */
  char *newarg;
  char *read_result;
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int result;
  int i;
  int n_opt;
  int n_read_file = 0; /* indicates -f switch */
  int n_cmdlinerror = 0;
  FILE *infilefp;
  FILE* errstream;
  struct lilimem sentinel;
  struct simplelistvals slvals;

  errstream = (n_cgi) ? stdout : stderr;

  slvals.outbuffer = outbuffer;

  strcpy(slvals.outbuffer, "updatejo ");
  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';

  strcpy(db, current_db); /* use default db if set */

  /* 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)+8));
  if (newarg == NULL) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (insert_lilimem(&sentinel, (void**)&newarg, NULL)) {
    delete_all_lilimem(&sentinel);
    return 1;
  }
  
  strcpy(newarg, "addlink ");
  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, "aA:c:C:d:e:E:f:F:g:G:hi:kl:L:o:O:p:Pqr:R:s:S:t:T:u: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 'd':
      /*        printf("-d %s\n", optarg); */
      strncpy(db, optarg, _POSIX_PATH_MAX - 1); /* override preset db */
      db[_POSIX_PATH_MAX-1] = '\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, NULL)) { */
/* 	  delete_all_lilimem(&sentinel); */
/* 	  return 1; */
/* 	} */
/* 	n_read_file = 1; */
/*       } */
/*       break; */
    case 'h':
      printf("Updates the synonyms of a given journal.\nSyntax: updatejo  [-c command] [-d database] [-h] [-o outfile] [-O outfile] :JO:|:JF:|:J1:|:J2:=journal :XY:=name [:XY:=name1...]\nThe first argument selects the journal by its short name, full name, user abbreviation1, or user abbreviation2, respectively\nXY is one of JO|JF|J1|J2 and sets the corresponding synonym to the supplied name\nOptions: -c command   pipe the output through command\n         -d database  specify the database to work with\n         -h           prints this mini-help\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\n");
      delete_all_lilimem(&sentinel);
      return 0;
      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;
      /* now all the options that main has already taken care of */
    case 'a':
    case 'A':
    case 'C': /* fall through */
    case 'e':
    case 'E':
    case 'f':
    case 'F':
    case 'g':
    case 'G':
    case 'i':
    case 'k':
    case 'l':
    case 'L':
    case 'p':
    case 'P':
    case 'q':
    case 'R':
    case 's':
    case 'S':
    case 't':
    case 'T':
    case 'u':
    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 (!*db) {
    fprintf(errstream, "Don't know which database to use. Select one with selectdb or use the -d switch with updatejo\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (n_cmdlinerror) {
    delete_all_lilimem(&sentinel);
    return 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)) {
    delete_all_lilimem(&sentinel);
    close(slvals.n_sockfd);
    return 1;
  }


  if (n_read_file || n_read_stdin) {
    if (!n_read_stdin) {
      infilefp = fopen(infile, "rb");
      if (infilefp == NULL) {
	send_status(slvals.n_sockfd, 112, TERM_NO);
	delete_all_lilimem(&sentinel);
	close(slvals.n_sockfd);
	return 1;
      }
    }
    else {
      infilefp = stdin; /* stdin is already open */
    }

    read_result = fgets(slvals.outbuffer+strlen(slvals.outbuffer), COMMAND_INBUF_LEN-strlen(slvals.outbuffer), infilefp);
    if (!n_read_stdin) { /* don't close stdin */
      fclose(infilefp);
    }
    if (read_result == NULL) {
      send_status(slvals.n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(slvals.n_sockfd);
      return 1;
    }
  }
  else {
    if (optind < inargc) {
      strcat(slvals.outbuffer, "\"");
      /*      printf("%d\n", optind); */
      for (i = optind; i < inargc; i++) {
/* 	printf("inargv[%d]: %s<<\n", i, inargv[i]); */
	/* the tokenizer returns a quoted item as a separate token
	   even if there is no space between e.g. a '=' and the
	   quoted item. To rectify this, check whether the previous
	   item ended with a '=' */
	if (slvals.outbuffer[strlen(slvals.outbuffer)-1] != '=') {
	  strcat(slvals.outbuffer, inargv[i]);
	  if (slvals.outbuffer[strlen(slvals.outbuffer)-1] != '=') {
	    strcat(slvals.outbuffer, " ");
	  }
	}
	else {
	  /* quote item */
	  strcat(slvals.outbuffer, "\'");

	  /* in order to allow e.g. single quotes in journal names we
	     must escape the string properly. It will be unescaped on
	     the server side */
	  escape_chars(slvals.outbuffer + strlen(slvals.outbuffer), inargv[i], strlen(inargv[i]), "'\"");
	  strcat(slvals.outbuffer, "\' ");
	}
      }
      strcpy(&slvals.outbuffer[strlen(slvals.outbuffer)-1], "\""); /* remove trailing space */
    }
  }

  /* ToDo: do we need this here? */
  /* make sure there's no newlines in the string; reuse read_result */
  for (read_result = slvals.outbuffer; *read_result; read_result++) {
    if (*read_result == '\n' || *read_result == '\r') {
      *read_result = ' ';
    }
  }

  /* assemble command string for refdbd */
  strcat(slvals.outbuffer, " -u ");
  strcat(slvals.outbuffer, username);
  if (*passwd) {
    strcat(slvals.outbuffer, " -w ");
    strcat(slvals.outbuffer, scrambled_passwd);
  }
  strcat(slvals.outbuffer, " -d ");
  strcat(slvals.outbuffer, db);

  LOG_PRINT(LOG_DEBUG, slvals.outbuffer);
  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;
  char* canpath;

  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;
  }
  else if (!strcmp(varname, "defaultris")) {
    if (*default_ris) {
      canpath = canonicalize_path(default_ris);
      if (!canpath || strlen(canpath) >= PREFS_BUF_LEN) {
	if (canpath) {
	  free(canpath);
	}
	return 0; /* do nothing, keep existing path */
      }
      strcpy(default_ris, canpath);
      free(canpath);
    }
    /* else: do nothing, no file selected */
    return 0;
  }
  else if (!strcmp(varname, "pdfroot")) {
    /* nothing to do */
    return 0;
  }
  else if (!strcmp(varname, "fields")) {
    /* nothing to do */
    return 0;
  }
  else if (!strcmp(varname, "autokill")) {
    /* nothing to do */
    return 0;
  }
  else if (!strcmp(varname, "defaultdb")) {
    /* nothing to do */
    return 0;
  }
  else if (!strcmp(varname, "cssurl")) {
    /* nothing to do */
    return 0;
  }
  else if (!strcmp(varname, "toencoding")) {
    /* nothing to do */
    return 0;
  }
  else if (!strcmp(varname, "fromencoding")) {
    /* nothing to do */
    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);
  }
}

#ifdef REFDB_CGI
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  alrmhandler(): handler for the SIGALRM signal. The app will exit.
                 Purpose of this behaviour is to kill hanging CGI
		 programs that could otherwise accumulate until the
		 server dies of memory exhaustion

  void alrmhandler

  int sig the received signal

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void alrmhandler(int sig) {
    LOG_PRINT(LOG_ERR, "received suicide alarm and exited");
    exit(2);
}
#endif






