/*++++++++++++++++++++
  refdbd.c: refdb application server
  markus@mhoenicka.de 2000-10-02

   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: linked list for socket descriptors looks ok, but does not have much effect as long as this app is not multithreaded. Test thoroughly again as soon as multithreading is implemented. Update: With multithreading still pending (and maybe never coming) and the new fork() way of doing things the linked list does not do much anyway. The overhead is small, so probably its best to just leave it there for the time being */

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#include <syslog.h> /* priority level definitions */
#include <time.h>
#include <signal.h>
#include <fcntl.h>
#include <dbi/dbi.h> /* database abstraction layer */
#include <errno.h>

#include "getopt.h"

#include "linklist.h"
#include "pref.h" /* depends on linklist.h */
#include "strfncs.h"
#include "refdb.h"
#include "backend.h"
#include "refdbd.h" /* depends on backend.h */
#include "tokenize.h"
#include "connect.h"
#include "readris.h"
#include "writeris.h" /* depends on backend.h */
#include "risdb.h"
#include "enigma.h" /* password encryption */
#include "cgi.h"
#include "dbfncs.h"
#include "refdbdupdb.h"
#include "refdbdgetref.h"
#include "refdbdgetrefx.h"
#include "backend-bibtex.h"
#include "passwd.h"

#ifndef HAVE_ATOLL
long long atoll(const char *str);
#endif

Prefs prefs[26] = {
  {"serverip", ""},
  {"timeout", ""},
  {"port", ""},
  {"dbsport", ""},
  {"logfile", ""},
  {"logdest", ""},
  {"loglevel", ""},
  {"remoteadmin", ""},
  {"pidfile", ""},
  {"refdblib", ""},
  {"dbserver", ""},
  {"defaultdb", ""},
  {"keep_pnames", ""},
  {"db_encoding", ""},
  {"dbi_driverdir", ""},
  {"keyword_scan", ""},
  {"dbpath", ""},
  {"upper_citekey", ""},
  {"remoteconnect", ""},
  {"in_encoding", ""},
  {"nongeek_offset", ""},
  {"db_timeout", ""},
  {"share_default", ""},
  {"main_db", ""},
  {"no_encrypt", ""},
  {"", ""}
};


char log_file[PREFS_BUF_LEN] = "/var/log/refdbd.log"; /* custom log file */
char log_dest[PREFS_BUF_LEN] = "2"; /* log destination. 0=print on stderr, 1=use syslog, 2=use custom logfile */
char log_level[PREFS_BUF_LEN] = "6"; /* the maximum priority that actually gets sent out - priorities are from 0 (only highest importance) to 7 (send all log info). -1 means nothing gets logged */
char pid_file[PREFS_BUF_LEN] = "/var/run/refdbd.pid"; /* default pid file */
char port_address[PREFS_BUF_LEN] = "9734"; /* default port */
char refdb_timeout[PREFS_BUF_LEN] = "180"; /* 3 minutes default timeout */
char remote_admin[PREFS_BUF_LEN] = "f"; /* remote administration not allowed */
char remote_connect[PREFS_BUF_LEN] = "f"; /* remote connections not allowed */
char refdblib[PREFS_BUF_LEN] = ""; /* path to the shareable RefDB files */
char default_db[PREFS_BUF_LEN] = ""; /* default RefDB database */
char main_db[PREFS_BUF_LEN] = "refdb"; /* default main database */
char keep_pnames[PREFS_BUF_LEN] = ""; /* whether to keep periodical names */
char upper_citekey[PREFS_BUF_LEN] = ""; /* whether to uppercase citation keys */
char default_db_encoding[PREFS_BUF_LEN] = ""; /* default char encoding for new databases */
char default_input_encoding[PREFS_BUF_LEN] = "UTF-8"; /* default char encoding for input data */
char keyword_scan[PREFS_BUF_LEN] = ""; /* run automatic keyword scan if 't' */
char ng_offset[PREFS_BUF_LEN] = "1"; /* 0 for geeks, 1 for humans */
char db_timeout[PREFS_BUF_LEN] = "60000"; /* db-engine specific timeout */
char share_default[PREFS_BUF_LEN] = "public"; /* default notes sharing */
char no_decrypt[PREFS_BUF_LEN] = ""; /* skip password decryption if 't' */

char confdir[_POSIX_PATH_MAX+1] = ""; /* path to the config files */
char dbi_driver_dir[_POSIX_PATH_MAX+1] = ""; /* path to the libdbi drivers */
char the_fifo[_POSIX_PATH_MAX] = ""; /* full path of fifo for child->parent msg*/
int n_refdb_timeout; /* timeout in seconds */
int n_verbose = 0; /* do we want logorrhoeic output? */
int run = 1; /* main will exit when this is set to 0 */
int n_log_level = 6; /* numeric version of log_level */
int n_log_dest = 1; /* numeric version of log_dest */
int n_remote_admin = 0; /* if 1, allow remote administration with refdba */
int n_remote_connect = 0; /* if 1, allow remote connections */
int n_reopen_log = 0; /* if 1, the log file will be reopened */
int fd_fifo = -1; /* file descriptor of the fifo (child->parent communication) */
int parent_pid; /* process id of parent process */
int gotsignal = 0; /* used by signal handlers to circumvent select failure */
int n_abort_connect = 0; /* used to interrupt socket select/read/write */
int child_exitval = 0; /* exit value of a child process */
int gotchldsig = 0; /* set to 1 if SIGCHLD received */
int n_cgi = 0; /* if 1, data are requested for cgi output */
int nongeek_offset = 1; /* 0 for geeks, 1 for humans */
FILE* fp_log_file = NULL; /* a FILE pointer to a custom log file */
dbi_result dbi_style_res = NULL; /* used by backends to load style information */
struct db_caps* ptr_dbcaps = NULL; /* info about database engine capabilities */

/* forward declarations of local functions */
static void sig_handler (int sig);
static void sighup_handler (int sig);
static void sigchld_handler(int sig);
static int postprocess_var(char* varname, char* string);
static void free_bibinfo(struct bibinfo* ptr_biblio_info);

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

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  the one and only main function

  int main

  int argc number of arguments

  char** argv ptr to array of ptrs to argument strings

  return codes (parent): 0 if successful, 1 if an error occurred

  return codes (child): 0 if successful
                        1 if an error occurred before starting to
                          process the client command
                        2 if an error occurred during processing
			  of the client command
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int main(int argc, char** argv) {
  int server_sockfd, client_sockfd; /* sockets used for client/server dialog */
  int server_len;
  int i, j, n_opt; /* flags, result, */
  int n_pipe_buf = 512; /* maximum size of atomic write to fifo. 512 is the posix value. if the OS defines a larger value, this will be used below */
  int num_drivers = 0; /* number of libdbi drivers */
  int n_have_requested_driver = 0; /* used to check the libdbi drivers */
  int is_first_child = 1; /* will be reset after fork, for child actions that should run only once per server instance */
  int n_updatedb = 0; /* if 1, update main database and exit */
  int n_checkdb = 0; /* if 1, check main database and exit */
  int n_nofork = 0; /* if 1, do not fork */
  socklen_t client_len;
  struct sockaddr_in server_address;
  struct sockaddr_in client_address;
  fd_set readfds, testfds;
  int readinit = 1;
  int n_standalone = 0; /* if 0, runs as daemon; if not 0, runs as app */
  size_t n_byte_written;
  char pid_buffer[32];
  char ip_or_path[PREFS_BUF_LEN] = "";
  char empty_string = '\0';
  char *msg_buffer; /* buffer for log messages */
  struct olili first_olili; /* ordered linked list for socket fds */
  char* fifo_buffer;
  struct sigaction new_action, old_action;
  struct sigaction new_hup_action, old_hup_action;
  struct sigaction new_chld_action, old_chld_action;
  struct CLIENT_REQUEST* ptr_clrequest;
  dbi_driver driver;

  /* signal stuff */
  new_action.sa_handler = sig_handler;
  sigemptyset(&new_action.sa_mask);
  new_action.sa_flags = 0;
  
  new_hup_action.sa_handler = sighup_handler;
  sigemptyset(&new_hup_action.sa_mask);
  new_hup_action.sa_flags = 0;
  
  new_chld_action.sa_handler = sigchld_handler;
  sigemptyset(&new_chld_action.sa_mask);
  new_chld_action.sa_flags = SA_NOCLDSTOP; /* ignore stopped children */
  
  if ((sigaction(SIGINT, &new_action, &old_action) != 0) ||
      (sigaction(SIGTERM, &new_action, &old_action) != 0)) {
    LOG_PRINT(LOG_CRIT, "initializing signal handler failed");
    exit(1);
  }

  if (sigaction(SIGHUP, &new_hup_action, &old_hup_action) != 0) {
    LOG_PRINT(LOG_CRIT, "initializing signal handler failed");
    exit(1);
  }

  if (sigaction(SIGCHLD, &new_chld_action, &old_chld_action) != 0) {
    LOG_PRINT(LOG_CRIT, "initializing signal handler failed");
    exit(1);
  }

  /* initialize CLIENT_REQUEST structure (wish I had a constructor) */
  if ((ptr_clrequest = new_client_request()) == NULL) {
    exit(1);
  }

  ptr_clrequest->ptr_optind = &optind;
  ptr_clrequest->argument = &empty_string;
  strcpy(ptr_clrequest->db_path, "/var/lib/refdb/db"); /* use the FHS value as default */

  /* initialize array of init file variables */
  prefs[0].varvalue = ptr_clrequest->server_ip;
  prefs[1].varvalue = refdb_timeout;
  prefs[2].varvalue = port_address;
  prefs[3].varvalue = ptr_clrequest->dbs_port_address;
  prefs[4].varvalue = log_file;
  prefs[5].varvalue = log_dest;
  prefs[6].varvalue = log_level;
  prefs[7].varvalue = remote_admin;
  prefs[8].varvalue = pid_file;
  prefs[9].varvalue = refdblib;
  prefs[10].varvalue = ptr_clrequest->dbserver;
  prefs[11].varvalue = default_db;
  prefs[12].varvalue = keep_pnames;
  prefs[13].varvalue = default_db_encoding;
  prefs[14].varvalue = dbi_driver_dir;
  prefs[15].varvalue = keyword_scan;
  prefs[16].varvalue = ptr_clrequest->db_path;
  prefs[17].varvalue = upper_citekey;
  prefs[18].varvalue = remote_connect;
  prefs[19].varvalue = default_input_encoding;
  prefs[20].varvalue = ng_offset;
  prefs[21].varvalue = db_timeout;
  prefs[22].varvalue = share_default;
  prefs[23].varvalue = main_db;
  prefs[24].varvalue = no_decrypt;

  *dbi_driver_dir = '\0';

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

  if (readinit) { /* -q was not used */
    /* read config file settings */
    read_prefs(prefs, "refdbdrc", 0);
  }

  /* read command line settings. These may override the config file settings */
  while ((n_opt = getopt(argc, argv, "ab:cd:D:e:E:hi:IkKl:L:m:M:np:P:qrsS:T:u:UvVw:xy:Y:")) != -1) {
    switch (n_opt) {
    case 'a':
      n_updatedb = 1;
      break;
    case 'b':
      strncpy(ptr_clrequest->dbs_port_address, optarg, PREFS_BUF_LEN);
      ptr_clrequest->dbs_port_address[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'c':
      n_checkdb = 1;
      break;
    case 'd':
      strncpy(default_db, optarg, PREFS_BUF_LEN);
      default_db[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'D':
      strncpy(ptr_clrequest->dbserver, optarg, PREFS_BUF_LEN);
      ptr_clrequest->dbserver[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'e':
      strncpy(log_dest, optarg, PREFS_BUF_LEN);
      log_dest[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'E':
      strncpy(default_db_encoding, optarg, PREFS_BUF_LEN);
      default_db_encoding[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'h':
      fprintf(stderr, "Usage: refdbd [-a] [-b dbs_port] [-c] [-d database] [-D dbserver] [-e dest] [-E encoding] [-h] [-i address] [-I] [-k] [-K] [-l level] [-L file] [-m time] [-M main_db] [-p port] [-P PIDfile] [-q] [-r] [-s] [-T time] [-u] [-v] [-V] [-y confdir] [-Y driverdir]\nOptions: -a update main database and exit\n         -b set database server port\n         -c check connection and main database and exit\n         -d set default reference database\n         -D select database server (mysql|pgsql|sqlite|sqlite3)\n         -e set log destination to dest (0-2)\n         -h prints this help\n         -i set server IP address to address\n         -I allow remote connections\n         -k keep periodical names when deleting references\n         -K activate automatic keyword scan\n         -l set the log level to level (0<=level<=7)\n         -L use file as log-file (full path)\n         -m set database timeout to time\n         -M set name of main database\n         -p set refdbd port\n         -P set path to PID file\n         -q ignore init-file\n         -r allow remote administration\n         -s run standalone, not as daemon\n         -S set notes sharing (public|private)\n         -T set timeout to time\n         -U uppercase citation keys (for SGML)\n         -v show version information\n         -V switch to verbose mode\n         -y look for configuration files in confdir\n         -Y look for dbi drivers in driverdir\n");
      free(ptr_clrequest);
      exit (0);
      break;
    case 'i':
      strncpy(ip_or_path, optarg, PREFS_BUF_LEN);
      ip_or_path[PREFS_BUF_LEN - 1] = '\0';
      break;
    case 'I':
      strcpy(remote_connect, "t");
      break;
    case 'k':
      strcpy(keep_pnames, "t");
      break;
    case 'K':
      strcpy(keyword_scan, "t");
      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 'm':
      strncpy(db_timeout, optarg, PREFS_BUF_LEN);
      db_timeout[PREFS_BUF_LEN - 1] = '\0';
      break;
    case 'M':
      strncpy(main_db, optarg, PREFS_BUF_LEN);
      main_db[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'n':
      n_nofork = 1;
      break;
    case 'p':
      strncpy(port_address, optarg, PREFS_BUF_LEN);
      port_address[PREFS_BUF_LEN - 1] = '\0';
      break;
    case 'P':
      strncpy(pid_file, optarg, PREFS_BUF_LEN);
      pid_file[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'q':
      readinit = 0;
      break;
    case 'r':
      strcpy(remote_admin, "t");
      break;
    case 's':
      n_standalone = 1;
      break;
    case 'S':
      strncpy(share_default, optarg, PREFS_BUF_LEN);
      share_default[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(ptr_clrequest->username, optarg, USERNAME_LENGTH);
      ptr_clrequest->username[USERNAME_LENGTH-1] = '\0';
      break;
    case 'U':
      strcpy(upper_citekey, "t");
      break;
    case 'v':
      printf("refdbd %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());
      free(ptr_clrequest);
      exit (0);
      break;
    case 'V':
      n_verbose = 1;
      break;
    case 'w':
      strncpy(ptr_clrequest->passwd, optarg, PREFS_BUF_LEN);
      ptr_clrequest->passwd[PREFS_BUF_LEN-1] = '\0';
      /* todo: allow '*' to hide password */
      break;
    case 'x':
      strcpy(no_decrypt, "t");
      break;
    case 'y':
      /* do nothing here, option is used before getopt runs */
      break;
    case 'Y':
      strncpy(dbi_driver_dir, optarg, _POSIX_PATH_MAX);
      dbi_driver_dir[_POSIX_PATH_MAX] = '\0';
      break;
    case ':':
      fprintf(stderr, "Usage: refdbd [-a] [-b dbs_port] [-c] [-d database] [-D dbserver] [-e dest] [-E encoding] [-h] [-i address] [-I] [-k] [-K] [-l level] [-L file] [-m time] [-p port] [-P PIDfile] [-q] [-r] [-s] [-T time] [-u] [-v] [-V] [-x] [-y confdir] [-Y driverdir]\nOptions: -a update main database and exit\n         -b set database server port\n         -c check connection and main database and exit\n         -D select database server (mysql|pgsql|sqlite|sqlite3)\n         -e set log destination to dest (0-2)\n         -h prints this help\n         -i set server IP address to address\n         -I allow remote connections\n         -k keep periodical names when deleting references\n         -K activate automatic keyword scan\n         -l set the log level to level (0<=level<=7)\n         -L use file as log-file (full path)\n         -m set database timeout to time\n         -p set refdbd port\n         -P set path to PID file\n         -q ignore init-file\n         -r allow remote administration\n         -s run standalone, not as daemon\n         -S set notes sharing (public|private)\n         -T set timeout to time\n         -U uppercase citation keys (for SGML)\n         -v show version information\n         -V switch to verbose mode\n         -x use unencrypted passwords\n         -y look for configuration files in confdir\n         -Y look for dbi drivers in driverdir\n");
      free(ptr_clrequest);
      exit (1);
      break;
    case '?':
      fprintf(stderr, "unknown option %c: use refdbd -h to display usage\n", optopt);
      free(ptr_clrequest);
      exit (1);
      break;
    }
  }
  
  /* translate some command line settings into numeric values */
  n_refdb_timeout = atoi(refdb_timeout);
  n_log_level = num_loglevel(log_level);
  n_log_dest = num_logdest(log_dest);
  n_remote_admin = (*remote_admin == 't') ? 1:0; 
  n_remote_connect = (*remote_connect == 't') ? 1:0; 
  nongeek_offset = atoi(ng_offset);
  ptr_clrequest->db_timeout = atoi(db_timeout);
  ptr_clrequest->share_default = (!strcmp(share_default, "public")) ? 1:0;

  /* uppercase the encoding names */
  strup(default_db_encoding);
  strup(default_input_encoding);

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

  /* see whether we need a password */
  postprocess_var("passwd", ptr_clrequest->passwd);

  if ((!strcmp(ptr_clrequest->dbserver, "sqlite")
       || !strcmp(ptr_clrequest->dbserver, "sqlite3"))
      && *ip_or_path) {
    strcpy(ptr_clrequest->db_path, ip_or_path);
  }
  else {
    if (*ip_or_path) {
      strcpy(ptr_clrequest->server_ip, ip_or_path);
    }

      i = check_ip(ptr_clrequest->server_ip); /* reuse i */
      if (i > 0) {
	if (i==1) {
	  fprintf(stderr, "\'%s\' cannot be resolved as a hostname\n", optarg);
	}
	else if (i==2) {
	  fprintf(stderr, "\'%s\' does not appear to be an IP host\n", optarg);
	}
	else if (i==3) {
	  fprintf(stderr, "\'%s\' does not appear to have a valid IP address\n", optarg);
	}
	else if (i==4) {
	  fprintf(stderr, "\'%s' has more than one network interface. Please specify one IP address\n", optarg);
	}
	free(ptr_clrequest);
	exit (1);
      }
      /* else: server_ip contains either a valid IP address, or 'localhost' */
  } /* end if not sqlite */

  if (!n_standalone && !n_updatedb && !n_checkdb) {
    if (daemonize(NULL)) { /* could not start as a daemon */
      LOG_PRINT(LOG_CRIT, "could not start as a daemon");
      free(ptr_clrequest);
      exit(1);
    }
  }

  parent_pid = getpid();

  /* write PID to pidfile */
  if ((fp_log_file = fopen(pid_file, "wb")) == NULL) { /* reuse fp_log_file */
    fprintf(stderr, "cannot open PID file\n");
    free(ptr_clrequest);
    exit(1);
  }
  else {
    sprintf(pid_buffer, "%d", parent_pid);
    if ((n_byte_written = fwrite(pid_buffer, sizeof(char), strlen(pid_buffer), fp_log_file)) < strlen(pid_buffer)) {
      fprintf(stderr, "cannot write to PID file\n");
    }
    fclose(fp_log_file);
    fp_log_file = NULL; /* reset file ptr */
  }

  /* set up logging */
  if (n_log_dest == 2) { /* use custom log file */
    if ((fp_log_file = fopen(log_file, "ab")) == NULL) {
      n_log_dest = 1; /* fall back to syslog */
      openlog("refdbd", 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("refdbd", LOG_PID|LOG_ODELAY, LOG_USER);
  }

/*      printf("level %d; dest %d, file %s\n", n_log_level, n_log_dest, log_file);  */

  /* get us some buffer for log messages */
  msg_buffer = malloc(MSG_BUF_SIZE);
  if (msg_buffer == NULL) {
    LOG_PRINT(LOG_CRIT, "Out of memory");
    free(ptr_clrequest);
    exit(1);
  }

  /* check database abstraction layer */
  LOG_PRINT(LOG_DEBUG, "dbi_driver_dir went to:");
  LOG_PRINT(LOG_DEBUG, dbi_driver_dir);
  if (*dbi_driver_dir == '/') {
    num_drivers = my_dbi_initialize(dbi_driver_dir, &(ptr_clrequest->inst));
    LOG_PRINT(LOG_DEBUG, "dbi is up using driver dir:");
    LOG_PRINT(LOG_DEBUG, dbi_driver_dir);
  }
  else {
    num_drivers = my_dbi_initialize(NULL, &(ptr_clrequest->inst));
    LOG_PRINT(LOG_DEBUG, "dbi is up using default driver dir");
  }

  if (num_drivers < 0) {
    LOG_PRINT(LOG_CRIT, "Unable to initialize libdbi! Make sure you specified a valid driver directory.");
    my_dbi_shutdown(ptr_clrequest->inst);
    free(ptr_clrequest);
    free(msg_buffer);
    exit(1);
  }
  else if (num_drivers == 0) {
    LOG_PRINT(LOG_ERR, "Initialized libdbi, but no drivers were found!");
    my_dbi_shutdown(ptr_clrequest->inst);
    free(ptr_clrequest);
    free(msg_buffer);
    exit(1);
  }
	
  driver = NULL;
  LOG_PRINT(LOG_INFO, "Available libdbi database drivers:");
  while ((driver = my_dbi_driver_list(driver, ptr_clrequest->inst)) != NULL) {
    /* check whether requested driver is available */
    if (!strcmp(ptr_clrequest->dbserver, dbi_driver_get_name(driver))) {
      n_have_requested_driver++;
    }
    LOG_PRINT(LOG_INFO, dbi_driver_get_name(driver));
  }
  
  if (!n_have_requested_driver) {
    LOG_PRINT(LOG_ERR, "Requested libdbi driver is not present");
    my_dbi_shutdown(ptr_clrequest->inst);
    free(ptr_clrequest);
    free(msg_buffer);
    exit(1);
  }

  LOG_PRINT(LOG_INFO, "Requested libdbi driver found:");
  LOG_PRINT(LOG_INFO, ptr_clrequest->dbserver);

  /* check database directory */
  LOG_PRINT(LOG_INFO, "Database directory:");
  LOG_PRINT(LOG_INFO, ptr_clrequest->db_path);

  /* update or check the main database version and exit */
  if (n_updatedb || n_checkdb) {
    int retval;

    if (*default_db_encoding) {
      strcpy(ptr_clrequest->db_encoding, default_db_encoding);
    }
    else {
      strcpy(ptr_clrequest->db_encoding, "UTF-8");
    }

    retval = update_main_db(ptr_clrequest, n_updatedb);
    if (retval) {
      if (n_updatedb) {
	fprintf(stderr, "maintenance of main database failed\n");
      }
      else {
	fprintf(stderr, "main database check failed\n");
      }
    }
    my_dbi_shutdown(ptr_clrequest->inst);
    free(ptr_clrequest);
    free(msg_buffer);
    if (fp_log_file != NULL) {
      fclose(fp_log_file); /* not strictly necessary here, but it's nicer */
    }
    exit(retval);
  }

  /* shutdown for the time being. Each child will initialize libdbi again */
  my_dbi_shutdown(ptr_clrequest->inst);

  LOG_PRINT(LOG_INFO, "application server started");

  if (ptr_clrequest->share_default) {
    LOG_PRINT(LOG_INFO, "share extended notes by default");
  }
  else {
    LOG_PRINT(LOG_INFO, "hide extended notes by default");
  }

  if (*default_db) {
    sprintf(msg_buffer, "use %s as default database", default_db);
    LOG_PRINT(LOG_DEBUG, msg_buffer);
  }

  /* add pid to fifo name. This way, multiple copies of refdbd can run */
  sprintf(the_fifo, "%s%d", FIFO_NAME, parent_pid);
  sprintf(msg_buffer, "use %s as fifo", the_fifo);
  LOG_PRINT(LOG_DEBUG, msg_buffer);

  /* prepare a fifo for child->parent communication */
  /*#ifdef HAVE_MKFIFO  */
#ifndef __CYGWIN__
  /* e.g. cygwin (1.1.7) does not implement mkfifo/named pipes */
  /* we emulate a fifo with a temporary file. To be polite, this is less
     than perfect, but it kinda works */
  /* create "fifo" if necessary and open for non-blocking read */
  if (access(the_fifo, F_OK) == -1) {
    if (mkfifo(the_fifo, 0777) != 0) {
      LOG_PRINT(LOG_WARNING, "could not create fifo");
    }
    else {
      fd_fifo = open(the_fifo, O_RDONLY | O_NONBLOCK);
    }
  }
  else {
    fd_fifo = open(the_fifo, O_RDONLY | O_NONBLOCK);
  }

  if (fd_fifo == -1) {
    LOG_PRINT(LOG_WARNING, "could not open fifo");
  }
#else
  if (access(the_fifo, F_OK) != -1) {
    unlink(the_fifo); /* most likely crash leftover */
  }
#endif /* HAVE_MKFIFO */

  /* get a fifo buffer for reading data */
#ifdef PIPE_BUF
  n_pipe_buf = PIPE_BUF; /* this may be larger than _POSIX_PIPE_BUF */
#endif

  fifo_buffer = malloc(n_pipe_buf);
  if (fifo_buffer == NULL) {
    LOG_PRINT(LOG_CRIT, "Out of memory");
    free(ptr_clrequest);
    free(msg_buffer);
    exit(1);
  }
  
  /* set up socket */
  server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
/*   printf("setting server socket to %d\n", server_sockfd); */
  server_address.sin_family = AF_INET;
  server_address.sin_addr.s_addr = htonl(INADDR_ANY);
  server_address.sin_port = htons((unsigned short int)atoi(port_address));
  server_len = sizeof(server_address);
  bind(server_sockfd, (struct sockaddr *)&server_address, server_len);

  listen(server_sockfd, 10);
  
/*    signal(SIGCHLD, SIG_IGN);  *//* don't wait til child completes */

  FD_ZERO(&readfds);
  FD_SET(server_sockfd, &readfds);

  /* initialize linked list for socket descriptors */
  first_olili.fd = server_sockfd;
  first_olili.ptr_next = NULL;

  while(run) {
    int numbyte, numbyte1; /* number of bytes read */
    int result;

    testfds = readfds;

    sprintf(msg_buffer, "server waiting n_max_fd=%d", max_olili(&first_olili));
    LOG_PRINT(LOG_INFO, msg_buffer);

    result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0,
		    (struct timeval *)0);

    /* if select returns because of a signal, the result will be -1. This
       gives us a chance to test for the received signals */
    if (result < 1) {
      if (run && gotsignal) {
	/* sending SIGHUP sends us here. We want to continue */
	read_prefs(prefs, "refdbdrc", 0);
	n_refdb_timeout = atoi(refdb_timeout);
  	n_log_level = atoi(log_level);
	n_log_dest = atoi(log_dest);
	n_reopen_log = 1; /* log file destination may have changed */ 
  	gotsignal = 0; 
    	continue;
      }
      else if (run && gotchldsig) { /* SIGCHLD */
	sprintf(msg_buffer, "child exited with code %d", child_exitval);
	LOG_PRINT(LOG_INFO, msg_buffer);
	gotchldsig = 0;
	continue;
      }
      else if (run && !gotsignal) {
	LOG_PRINT(LOG_CRIT, "select failed");
	free(msg_buffer);
	free(fifo_buffer);
	if (n_log_dest == 1) {
	  closelog();
	}
	if (fd_fifo != -1) {
	  close(fd_fifo);
	  unlink(the_fifo);
	}
	free(ptr_clrequest);
	exit(1);
      }
      else{
	/* pressing Ctrl-c throws us here */
	LOG_PRINT(LOG_INFO, "server exited gracefully");
	free(msg_buffer);
	free(fifo_buffer);
	if (n_log_dest == 1) {
	  closelog();
	}
	if (fd_fifo != -1) {
	  close(fd_fifo);
	  unlink(the_fifo);
	}
	free(ptr_clrequest);
	exit(0);
      }
    } /* end if got signal */

    /* loop over all reasonable file descriptors */
    for (ptr_clrequest->fd = 3; ptr_clrequest->fd <= max_olili(&first_olili); ptr_clrequest->fd++) {
      /*#ifndef HAVE_MKFIFO*/
#ifdef __CYGWIN__
      if (access(the_fifo, F_OK) != -1) {
	if ((fd_fifo = open(the_fifo, O_RDWR)) == -1) {
	  /* pseudo-fifo is still open, give child a chance to finish */
	  sleep(1);
	  if ((fd_fifo = open(the_fifo, O_RDWR)) != -1) {
	    if ((numbyte = read(fd_fifo, fifo_buffer, n_pipe_buf)) > 0) {
	      if (fifo_buffer[numbyte] != '\0') {
		sleep(1);
		if ((numbyte1 = read(fd_fifo, (void*)fifo_buffer+numbyte, n_pipe_buf)) > 0) {
		  if (fifo_buffer[numbyte+numbyte1] != '\0') {
		    LOG_PRINT(LOG_WARNING, "incomplete message - confserv aborted");
		  }
		  else {
		    confserv(fifo_buffer, ptr_clrequest->server_ip);
		  }
		}
		else {
		  LOG_PRINT(LOG_WARNING, "incomplete message - confserv aborted");
		}
	      }
	      else {
		confserv(fifo_buffer, ptr_clrequest->server_ip);
	      }
	      close(fd_fifo);
	      unlink(the_fifo);
	    }
	  }
	  /* else: forget about it this time */
	}
	else { /* open fifo ok */
	  if ((numbyte = read(fd_fifo, fifo_buffer, n_pipe_buf)) > 0) {
	    if (fifo_buffer[numbyte-1] != '\0') {
	      sleep(1);
	      if ((numbyte1 = read(fd_fifo, (void*)fifo_buffer+numbyte, n_pipe_buf)) > 0) {
		if (fifo_buffer[numbyte+numbyte1-1] != '\0') {
		  LOG_PRINT(LOG_WARNING, "incomplete message - confserv aborted");
		}
		else {
		  confserv(fifo_buffer, ptr_clrequest->server_ip);
		}
	      }
	      else {
		LOG_PRINT(LOG_WARNING, "incomplete message - confserv aborted");
	      }
	    }
	    else {
	      confserv(fifo_buffer, ptr_clrequest->server_ip);
	    }
	    close(fd_fifo);
	    unlink(the_fifo);
	  }
	}
      }
      /* else: nothing to do */

#else
      if ((numbyte = read(fd_fifo, (void*)fifo_buffer, n_pipe_buf)) > 0) {
	/* confserv() changed some value */
	if (fifo_buffer[numbyte-1] != '\0') { /* if message incomplete */
/*  	  printf(fifo_buffer); */
	  sleep(1); /* give child a chance to complete the message */
	  if ((numbyte1 = read(fd_fifo, (void*)(fifo_buffer + numbyte), n_pipe_buf)) > 0) {
	    /* don't use a loop to try reading indefinitely as this would hang the server if the client crashed during writing to the fifo */
	    if (fifo_buffer[numbyte+numbyte1-1] != '\0') {
	      LOG_PRINT(LOG_WARNING, "incomplete message - confserv aborted");
	    }
	    else {
	      confserv(fifo_buffer, ptr_clrequest->server_ip);
	    }
	  }
	  else {
	    LOG_PRINT(LOG_WARNING, "incomplete message - confserv aborted");
	  }
	}
	else {
	  confserv(fifo_buffer, ptr_clrequest->server_ip);
	}
      }
      /* else: no message pending */
#endif /* HAVE_MKFIFO */

      if (!run) {
	break;
      }
      /* if some activity was detected on one of the file descriptors */
      if (FD_ISSET(ptr_clrequest->fd, &testfds)) {
	/* this must be a client knocking on the door */
	if (ptr_clrequest->fd == server_sockfd) {
	  client_len = sizeof(client_address);
	  client_sockfd = accept(server_sockfd,
				 (struct sockaddr *)&client_address,
				 &client_len);
	  if (client_sockfd == -1) {  /* accept failed */
	    LOG_PRINT(LOG_WARNING, "accept failed");
/* 	    printf("errno went to %d\n", errno); */
	    continue; /* try another fd. Quitting here seems a bit harsh,
                         but might be more honest */
	  }
	  FD_SET(client_sockfd, &readfds);

	  gethostname(ptr_clrequest->my_hostname, 256);
	  strcpy(ptr_clrequest->client_ip, inet_ntoa(client_address.sin_addr));
	  sprintf(msg_buffer, "adding client %s on fd %d", ptr_clrequest->client_ip, client_sockfd);
	  LOG_PRINT(LOG_INFO, msg_buffer);
	  insert_olili(&first_olili, client_sockfd);
	}

	/* this is a real request from an existing client */
	else {
	  /* write log data to disk. otherwise the child still has
	     all previous messages in its buffer and repeats them */
	  if (fp_log_file != NULL) {
	    fflush(fp_log_file);
	  }

	  if (n_nofork || fork() == 0) { /* we must be child, handle request */
	    char format_string[MAX_FMT_LEN] = "";
	    char sort_string[MAX_FMT_LEN] = "";
	    char set_owner[PREFS_BUF_LEN] = "";
/* 	    char db_encoding[PREFS_BUF_LEN] = ""; */
	    char user_host[HOSTNAME_LENGTH] = "";
	    char newuser_passwd[PASSWD_LENGTH+1] = "";
	    char wheelpos[13] = ""; /* pseudo-random string for password encryption */
	    char descrambled_passwd[PASSWD_LENGTH+1] = "";
	    char numberpasswd[PASSWD_LENGTH*3+1] = "";
	    char newuser_numberpasswd[PASSWD_LENGTH*3+1] = "";
	    char *child_msg_buffer = NULL;
	    char *child_inbuffer = NULL;
	    char *child_inbuffer1 = NULL;
	    char *child_returnmsg = NULL;
	    int n_dbi_is_up = 0;
	    int n_client_status;
	    int n_privatelist; /* if 1, search only personal interest list */
	    int n_personal; /* add only personal information */
	    int n_keep_id; /* keep ID of existing references in U5 */
	    int n_read_done;
	    int n_malloc_failed;
	    int n_cmdlinerror = 0;
	    int n_inbuf_size;
	    int n_remove;
	    int n_readonly = 0;
	    int retval = 0;
	    int inargcmax; /* maximum number of tokens */
	    int ref_format = 0; /* format for output */
	    int in_format = 0; /* format for input */
	    int n_all = 0; /* getjournal: return all synonyms if 1 */
	    int n_frequency = 0; /* if 1, provide frequency info */
	    size_t msg_size;

	    struct ADDRESULT* ptr_addresult; /* will hold result of addref */

	    struct bibinfo* ptr_biblio_info = NULL; /* bibliographic info for getref */
	    struct CLIENT_REQUEST* ptr_child_clrequest = NULL;
	    Lilid id_sentinel; /* linked list for addref keyword scan */

	    id_sentinel.ptr_next = NULL;

	    if ((ptr_addresult = new_addresult(1024)) == NULL
		|| (ptr_biblio_info = new_bibinfo(format_string, sort_string)) == NULL
		|| (ptr_child_clrequest = dup_client_request(ptr_clrequest)) == NULL) {
	      /* out of memory */
	      tiwrite(ptr_child_clrequest->fd, get_status_string(801), TERM_NO);
	      LOG_PRINT(LOG_CRIT, get_status_msg(801));
	      retval = 1;
	      goto cleanup;
	    }

	    strcpy(ptr_child_clrequest->current_db, default_db);
	    ptr_child_clrequest->argument = &empty_string;

	    /* use one of the following options to debug the child process */
/* 	    printf("child sleeps PID=%d\n", getpid()); */
/* 	    sleep(10); */
	    /* alternatively you can sleep until */
	    /* an explicit user action (i.e., kill -CONT or "gdb, c"), */
	    /* using this: */
	    /* kill (getpid(), SIGSTOP); */

	    n_read_done = 0;
	    n_inbuf_size = COMMAND_INBUF_LEN;
	    n_malloc_failed = 0;

	    /* get us some buffer for log messages */
	    child_msg_buffer = malloc(MSG_BUF_SIZE);
	    if (child_msg_buffer == NULL) {
	      /* out of memory */
	      tiwrite(ptr_child_clrequest->fd, get_status_string(801), TERM_NO);
	      LOG_PRINT(LOG_CRIT, get_status_msg(801));
	      retval = 1;
	      goto cleanup;
	    }

	    /* check client IP - NB we need child_msg_buf so we
	       can't check earlier than here */
	    if (!n_remote_connect && strcmp(ptr_child_clrequest->client_ip, "127.0.0.1")) {
	      retval = 1;
	      LOG_PRINT(LOG_WARNING, "refused remote connection");
	      goto cleanup;
	    }

	    child_inbuffer = malloc((size_t)COMMAND_INBUF_LEN*sizeof(char));
	    if (child_inbuffer == NULL) {
	      /* out of memory */
	      tiwrite(ptr_child_clrequest->fd, get_status_string(801), TERM_NO);
	      LOG_PRINT(LOG_CRIT, get_status_msg(801));
	      retval = 1;
	      goto cleanup;
	    }
	      
	    child_inbuffer1 = malloc((size_t)COMMAND_INBUF_LEN*sizeof(char));
	    if (child_inbuffer1 == NULL) {
	      /* out of memory */
	      tiwrite(ptr_child_clrequest->fd, get_status_string(801), TERM_NO);
	      LOG_PRINT(LOG_CRIT, get_status_msg(801));
	      retval = 1;
	      goto cleanup;
	    }
	      
	    msg_size = MSG_BUF_SIZE;
	    child_returnmsg = malloc(msg_size);
	    if (child_returnmsg == NULL) {
	      /* out of memory */
	      tiwrite(ptr_child_clrequest->fd, get_status_string(801), TERM_NO);
	      LOG_PRINT(LOG_CRIT, get_status_msg(801));
	      retval = 1;
	      goto cleanup;
	    }
	    child_returnmsg[0] = '\0';
	    LOG_PRINT(LOG_DEBUG, "try to read from client");

	    numbyte = tread(ptr_child_clrequest->fd, child_inbuffer, COMMAND_INBUF_LEN-1);
	    if (get_trailz(child_inbuffer, numbyte) < TERM_LEN || strlen(child_inbuffer) > PROTOCOL_LENGTH) { /* if transmission incomplete or protocol version string too long */
	      LOG_PRINT(LOG_CRIT, get_status_msg(103)); /* invalid client request */
	      LOG_PRINT(LOG_CRIT, child_inbuffer);
	      tiwrite(ptr_child_clrequest->fd, get_status_string(103), TERM_NO);
	      retval = 1;
	      goto cleanup;
	    }

	    /* child_inbuffer now contains a string that specifies the
	       protocol version that the client wants to use. We use
	       this information to silently adapt to the client's
	       protocol if possible */

	    sprintf(child_msg_buffer, "serving client on fd %d with protocol version %s", ptr_child_clrequest->fd, child_inbuffer);
	    LOG_PRINT(LOG_INFO, child_msg_buffer);

	    /* save protocol identifier in client request structure */
	    ptr_child_clrequest->protocol = atoi(child_inbuffer);

	    /* check protocol version */
	    if (ptr_child_clrequest->protocol < REFDB_MIN_CLIENT_PROTOCOL) {
	      send_status(ptr_child_clrequest->fd, 102, TERM_NO); /* protocol versions do not match */
	      retval = 1;
	      goto cleanup;
	    }

	    /* start of the password dialog */
	    randomize_wheels(wheelpos);
	    LOG_PRINT(LOG_DEBUG, wheelpos);

	    /* send pseudo-random string to client */
	    send_status(ptr_child_clrequest->fd, 0, TERM_NO);
	    tiwrite(ptr_child_clrequest->fd, wheelpos, TERM_YES);
	    LOG_PRINT(LOG_DEBUG, "send pseudo-random string to client");

	    n_client_status = read_status(ptr_child_clrequest->fd);

	    if (n_client_status) {
	      /* client aborted command */
	      LOG_PRINT(LOG_CRIT, get_status_msg(n_client_status));
	      retval = 1;
	      goto cleanup;
	    }

	    numbyte = tread(ptr_child_clrequest->fd, child_inbuffer, COMMAND_INBUF_LEN-1);
	    if (get_trailz(child_inbuffer, numbyte) < TERM_LEN) { /* if transmission incomplete */
	      /* incomplete client command */
	      LOG_PRINT(LOG_CRIT, get_status_msg(104));
	      send_status(ptr_child_clrequest->fd, 104, TERM_NO);
	      retval = 1;
	      goto cleanup;
	    }

	    /* end of the password dialog */

	    /* parse the client request. first we cut the request
	       into pieces with strtok(), then we use getopt() to interpret.
	       This code works only with GNU getopt(), not with BSD getopt() */

	    /* get a buffer to hold the tokens. Start with 10 tokens,
	       increase in steps of 10 as needed */
	    ptr_child_clrequest->inargc = 0;
	    inargcmax = 10;
	    ptr_child_clrequest->inargv = malloc((size_t)inargcmax*sizeof(char*));
	    if (ptr_child_clrequest->inargv == NULL) {
	      LOG_PRINT(LOG_CRIT, get_status_msg(801));
	      send_status(ptr_child_clrequest->fd, 801, TERM_NO);
	      retval = 1;
	      goto cleanup;
	    }

  	    LOG_PRINT(LOG_DEBUG, child_inbuffer); 

	    result = cmdln_tokenize(&(ptr_child_clrequest->inargc), &(ptr_child_clrequest->inargv), inargcmax, child_inbuffer);

	    if (result == 1) { /* memory error */
	      LOG_PRINT(LOG_CRIT, get_status_msg(801));
	      send_status(ptr_child_clrequest->fd, 801, TERM_NO);
	      retval = 1;
	      goto cleanup;
	    }
	    else if (result == 2) { /* empty command line */
	      LOG_PRINT(LOG_WARNING, get_status_msg(105));
	      send_status(ptr_child_clrequest->fd, 105, TERM_NO);
	      retval = 1;
	      goto cleanup;
	    }

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

	    /* reset optional arguments */
	    *set_owner = '\0';
	    *(ptr_child_clrequest->pdfroot) = '\0';
	    *(ptr_child_clrequest->cgi_url) = '\0';
	    *(ptr_child_clrequest->passwd) = '\0';
	    *numberpasswd = '\0';
	    *(ptr_child_clrequest->username) = '\0';
	    strcpy(ptr_child_clrequest->current_db, default_db);
	    *(ptr_child_clrequest->listname) = '\0';
	    *(ptr_child_clrequest->check_string) = '\0';
	    *(ptr_child_clrequest->namespace) = '\0';
	    *newuser_passwd = '\0';
	    *newuser_numberpasswd = '\0';
	    *user_host = '\0';
	    *format_string = '\0';
	    *sort_string = '\0';
/* 	    strcpy(db_encoding, default_db_encoding); */
	    *(ptr_child_clrequest->db_encoding) = '\0';
	    n_privatelist = 0;
	    n_remove = 0;
	    n_readonly = 0;
	    n_personal = ADDREF_UPDATE;
	    n_keep_id = 0;
	    n_all = 0;
	    ref_format = 0;

	    /* get options */
	    optind = 0;

	    while ((n_opt = getopt(ptr_child_clrequest->inargc, ptr_child_clrequest->inargv, "aA:b:Bc:d:E:G:H:kn:N:o:pPQrR:s:S:t:u:U:w:W:")) != -1) {
	      switch(n_opt) {
	      case 'a':
		n_all = 1;
		break;
	      case 'A':
		if (strcmp(optarg, "ris") == 0) {
		  in_format = REFRIS;
		}
		else if (strcmp(optarg, "risx") == 0) {
		  in_format = RISX;
		}
		break;
	      case 'b':
		strncpy(ptr_child_clrequest->listname, optarg, PREFS_BUF_LEN);
		ptr_child_clrequest->listname[PREFS_BUF_LEN] = '\0';
		break;
	      case 'B':
		n_readonly = 1;
		break;
	      case 'c':
		strncpy(ptr_child_clrequest->check_string, optarg, MAX_FMT_LEN-1);
		ptr_child_clrequest->check_string[MAX_FMT_LEN-1]='\0'; /* if string was truncated */
		break;
	      case 'd':
		strncpy(ptr_child_clrequest->current_db, optarg, DBNAME_LENGTH);
		ptr_child_clrequest->current_db[DBNAME_LENGTH] = '\0';
		break;
	      case 'E':
		/* used for both createdb and getref */
		strncpy(ptr_child_clrequest->db_encoding, optarg, PREFS_BUF_LEN);
		ptr_child_clrequest->db_encoding[PREFS_BUF_LEN-1] = '\0';
		strup(ptr_child_clrequest->db_encoding);
		break;
	      case 'G':
		strncpy(ptr_child_clrequest->cgi_url, optarg, _POSIX_PATH_MAX);
		ptr_child_clrequest->cgi_url[_POSIX_PATH_MAX] = '\0';
		break;
	      case 'H':
		strncpy(user_host, optarg, HOSTNAME_LENGTH);
		user_host[HOSTNAME_LENGTH - 1] = '\0';
		break;
	      case 'k':
		n_keep_id = 1;
		break;
	      case 'n':
		strncpy(ptr_child_clrequest->namespace, optarg, PREFS_BUF_LEN);
		ptr_child_clrequest->namespace[PREFS_BUF_LEN-1] = '\0';
		break;
	      case 'N':
		strncpy(ptr_child_clrequest->limit, optarg, PREFS_BUF_LEN);
		ptr_child_clrequest->limit[PREFS_BUF_LEN-1] = '\0';
		break;
	      case 'o':
		ptr_biblio_info->n_startnumber = (unsigned long long)atoll(optarg);
		break;
	      case 'p':
		n_personal = ADDREF_UPDATE_PERSONAL;
		break;
	      case 'P':
		n_privatelist = 1;
		break;
	      case 'Q':
		n_frequency = 1;
		break;
	      case 'r':
		n_remove = 1;
		break;
	      case 'R':
		strncpy(ptr_child_clrequest->pdfroot, optarg, _POSIX_PATH_MAX);
		ptr_child_clrequest->pdfroot[_POSIX_PATH_MAX] = '\0';
		break;
	      case 's':
		strncpy(format_string, optarg, MAX_FMT_LEN-1);
		format_string[MAX_FMT_LEN-1]='\0'; /* if string was truncated */
		break;
	      case 'S':
		strncpy(sort_string, optarg, MAX_FMT_LEN-1);
		sort_string[MAX_FMT_LEN-1]='\0'; /* if string was truncated */
		break;
	      case 't':
		if (strcmp(optarg, "html") == 0) {
		  ref_format = REFHTML;
		}
		else if (strcmp(optarg, "xhtml") == 0) {
		  ref_format = REFXHTML;
		}
		else if (strcmp(optarg, "ris") == 0) {
		  ref_format = REFRIS;
		}
		else if (strcmp(optarg, "db31") == 0) {
		  ref_format = REFDOCBK;
		}
		else if (strcmp(optarg, "bibtex") == 0) {
		  ref_format = REFBIBTEX;
		}
		/* ToDo: this one looks obsolete */
		else if (strcmp(optarg, "dbib") == 0) {
		  ref_format = 5;
		}
		else if (strcmp(optarg, "cgi") == 0) {
		  ref_format = REFCGIHTML;
		}
		else if (strcmp(optarg, "cgiris") == 0) {
		  ref_format = REFCGIRIS;
		}
		else if (strcmp(optarg, "db31x") == 0) {
		  ref_format = REFDOCBKX;
		}
		else if (strcmp(optarg, "db50x") == 0) {
		  ref_format = REFDOCBKX5;
		}
		else if (strcmp(optarg, "teix") == 0) {
		  ref_format = REFTEIX;
		}
		else if (strcmp(optarg, "tei5x") == 0) {
		  ref_format = REFTEIX5;
		}
		else if (strcmp(optarg, "rtf") == 0) {
		  ref_format = REFRTF;
		}
		else if (strcmp(optarg, "risx") == 0) {
		  ref_format = RISX;
		}
		else if (strcmp(optarg, "mods") == 0) {
		  ref_format = REFMODS;
		}
		else if (strcmp(optarg, "xnote") == 0) {
		  ref_format = XNOTE;
		}
		else if (strcmp(optarg, "citlist") == 0) {
		  ref_format = REFCITATIONLISTX;
		}
		else { /* default is screen */
		  ref_format = REFSCREEN;
		}
		break;
	      case 'u':
		strncpy(ptr_child_clrequest->username, optarg, USERNAME_LENGTH);
		ptr_child_clrequest->username[USERNAME_LENGTH - 1] = '\0';
		break;
	      case 'U':
		strncpy(set_owner, optarg, PREFS_BUF_LEN);
		set_owner[PREFS_BUF_LEN - 1] = '\0';
		break;
	      case 'w':
		strncpy(numberpasswd, optarg, PASSWD_LENGTH*3+1);
		numberpasswd[PASSWD_LENGTH*3] = '\0';
		break;
	      case 'W':
		strncpy(newuser_numberpasswd, optarg, PASSWD_LENGTH*3+1);
		newuser_numberpasswd[PASSWD_LENGTH*3] = '\0';
		break;
	      case ':':
		LOG_PRINT(LOG_WARNING, get_status_msg(106));
		send_status(ptr_child_clrequest->fd, 106, TERM_NO);
		n_cmdlinerror = 1;
		retval = 1;
		goto cleanup;
/* 		break; */
	      case '?':
		LOG_PRINT(LOG_WARNING, get_status_msg(107));
		send_status(ptr_child_clrequest->fd, 107, TERM_NO);
		n_cmdlinerror = 1;
		retval = 1;
		goto cleanup;
/* 		break; */
	      }
	    }
	    
	    if (*(ptr_child_clrequest->listname)
		&& strncmp(ptr_child_clrequest->listname, ptr_child_clrequest->username, strlen(ptr_child_clrequest->username))) {
	      /* prefix the name with the username to put it into
		 a separate namespace */
	      char prefixed_listname[PREFS_BUF_LEN+USERNAME_LENGTH+2];

	      strcpy(prefixed_listname, ptr_child_clrequest->username);
	      strcat(prefixed_listname, "-");
	      strcat(prefixed_listname, ptr_child_clrequest->listname);
	      /* write back result into structure member */
	      strcpy(ptr_child_clrequest->listname, prefixed_listname);
	    }

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

	    /* set a few clrequest variables */
	    ptr_child_clrequest->argument = (optind < ptr_child_clrequest->inargc) ? ptr_child_clrequest->inargv[optind] : &empty_string;
	    ptr_child_clrequest->n_cgi = (ref_format == 6 || ref_format == 7) ? 1 : 0;
	    ptr_biblio_info->encoding = ptr_child_clrequest->db_encoding;

	    /* descramble password */
	    if (*no_decrypt != 't') {
	      denumberize_passwd(ptr_child_clrequest->passwd, numberpasswd);
	      if (enigma_encrypt(ptr_child_clrequest->passwd, descrambled_passwd, wheelpos)) {
		LOG_PRINT(LOG_ERR, get_status_msg(108));
		send_status(ptr_child_clrequest->fd, 108, TERM_NO);
		retval = 1;
		goto cleanup;
	      }
	      else {
		/*    	      printf("encrypted:%s<< plain:%s<<\n", passwd, descrambled_passwd);  */
		strcpy(ptr_child_clrequest->passwd, descrambled_passwd);
	      }

	      /* descramble newuser password if there is one */
	      if (newuser_numberpasswd[0]) {
		denumberize_passwd(newuser_passwd, newuser_numberpasswd);
		if (enigma_encrypt(newuser_passwd, descrambled_passwd, wheelpos)) {
		  LOG_PRINT(LOG_ERR, get_status_msg(108));
		  send_status(ptr_child_clrequest->fd, 108, TERM_NO);
		  retval = 1;
		  goto cleanup;
		}
		else {
		  /*    	      printf("encrypted:%s<< plain:%s<<\n", passwd, descrambled_passwd);  */
		  strcpy(newuser_passwd, descrambled_passwd);
		}
	      }
	    }
	    else {
	      /* copy unencrypted passwords */
	      strncpy(ptr_child_clrequest->passwd, numberpasswd, PREFS_BUF_LEN);
	      (ptr_child_clrequest->passwd)[PREFS_BUF_LEN-1] = '\0';

	      /* todo: ptr_child_clrequest->passwd and newuser_passwd
		 have different maximum lenghts, they should probably
		 be coerced to have the same length */
	      strncpy(newuser_passwd, newuser_numberpasswd, PASSWD_LENGTH+1);
	      newuser_passwd[PASSWD_LENGTH] = '\0';
	    }


	    /* initialize database abstraction layer. If supported by
	       the libdbi version in use, create our own instance */
	    if (*dbi_driver_dir == '/') {
	      my_dbi_initialize(dbi_driver_dir, &(ptr_child_clrequest->inst));
	    }
	    else {
	      my_dbi_initialize(NULL, &(ptr_child_clrequest->inst));
	    }

	    LOG_PRINT(LOG_INFO, "dbi is up");
	    n_dbi_is_up++;

	    /* check presence of main database and fiddle with version file */
	    {
	      dbi_conn conn; /* libdbi connection structure */
	      dbi_result dbires; /* libdbi result structure */
	      char sql_command[] = "SELECT meta_dbversion from "MAIN_META" WHERE meta_type='refdb'";
	      char dbversionfile[_POSIX_PATH_MAX+1];
	      short int n_main_dbversion;
	      char main_dbversion[8];
	      FILE* version_fp;

	      if ((conn = connect_to_db(ptr_child_clrequest, main_db, 0)) != NULL) {
		/* run this only once per parent session */
		if (is_first_child) {
		  /* check version tag of main database */
		  dbires = dbi_conn_query(conn, sql_command);
		  if (!dbires || !dbi_result_next_row(dbires)) {
		    /* too old or corrupt */
		    LOG_PRINT(LOG_ERR, get_status_msg(203));
		    send_status(ptr_child_clrequest->fd, 203, TERM_NO);
		    retval = 1;
		    goto cleanup_db;
		  }
      
		  n_main_dbversion = dbi_result_get_short(dbires, "meta_dbversion");
		
		  if (my_dbi_conn_error_flag(conn)) {
		    /* too old or corrupt */
		    LOG_PRINT(LOG_ERR, get_status_msg(203));
		    send_status(ptr_child_clrequest->fd, 203, TERM_NO);
		    dbi_result_free(dbires);
		    retval = 1;
		    goto cleanup_db;
		  }

		  dbi_result_free(dbires);

		  if (n_main_dbversion < MIN_MAIN_DB_VERSION) {
		    LOG_PRINT(LOG_ERR, get_status_msg(206));
		    send_status(ptr_child_clrequest->fd, 206, TERM_NO);
		    retval = 1;
		    goto cleanup_db;
		  }

		  /* update database version file */
		  strncpy(dbversionfile, ptr_child_clrequest->db_path, _POSIX_PATH_MAX);
		  dbversionfile[_POSIX_PATH_MAX] = '\0';
		  strncat(dbversionfile, "/DB_VERSION", _POSIX_PATH_MAX);
		  dbversionfile[_POSIX_PATH_MAX] = '\0';

		  version_fp = fopen(dbversionfile, "w");

		  if (version_fp) {
		    sprintf(main_dbversion, "%d", n_main_dbversion);
		    if (fwrite(main_dbversion, strlen(main_dbversion)*sizeof(char), 1, version_fp)) {
		      LOG_PRINT(LOG_INFO, "updated version file:");
		      LOG_PRINT(LOG_ERR, dbversionfile);
		    }
		    fclose(version_fp);
		  }
		  else {
		    LOG_PRINT(LOG_ERR, "could not open version file:");
		    LOG_PRINT(LOG_ERR, dbversionfile);
		  }
		}
	      }
	      else {
		LOG_PRINT(LOG_WARNING, get_status_msg(202));
		send_status(ptr_child_clrequest->fd, 202, TERM_NO);
		retval = 1;
		goto cleanup_db;
	      }
	      LOG_PRINT(LOG_DEBUG, "Main database looks ok:");
	      LOG_PRINT(LOG_DEBUG, main_db);
	    }

	    /* get and initialize structure with database engine
	       capabilities */
	    if ((ptr_dbcaps = new_db_caps(ptr_child_clrequest)) == NULL) {
	      /* out of memory */
	      LOG_PRINT(LOG_CRIT, get_status_msg(801));
	      send_status(ptr_child_clrequest->fd, 801, TERM_NO);
	      retval = 1;
	      goto finish;
	    }

	    /* test for commands (still in inargv[0]) */

	    /*********************************************** viewstat ****/
	    if (strcmp((ptr_child_clrequest->inargv)[0], "viewstat") == 0) {
	      retval = viewstat(ptr_child_clrequest);

	      if (!retval) {
		strcpy(child_returnmsg, "1\n");
	      }
	      else if (retval == 2) {
		strcpy(child_returnmsg, "0\n");
	      }

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
	      }	      
	    }

	    /*********************************************** listdb ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "listdb") == 0) {
	      retval = listdb(ptr_child_clrequest, 0, ptr_addresult);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* summary for client */
		sprintf(child_returnmsg, ULLSPEC"\n", (unsigned long long)ptr_addresult->success);
	      }
	    }

	    /*********************************************** listuser ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "listuser") == 0) {

	      retval = listuser(ptr_child_clrequest, ptr_addresult);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* summary for client */
		sprintf(child_returnmsg, ULLSPEC"\n", (unsigned long long)ptr_addresult->success);
	      }
	    }

	    /*********************************************** liststyle ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "liststyle") == 0) {

	      retval = liststyle(ptr_child_clrequest, ptr_addresult);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" retrieved:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->failure));
	      }	      
	    }

	    /*********************************************** listword ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "listword") == 0) {

	      retval = listword(ptr_child_clrequest, ptr_addresult);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" retrieved:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->failure));
	      }	      
	    }

	    /*********************************************** whichdb ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "whichdb") == 0) {

	      retval = whichdb(ptr_child_clrequest);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* summary for client */
		strcpy(child_returnmsg, "1\n");
	      }
	    }

	    /*********************************************** createdb ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "createdb") == 0) {

	      if (!*(ptr_child_clrequest->db_encoding)) {
		strcpy(ptr_child_clrequest->db_encoding, default_db_encoding);
	      }

	      /* test whether we have a filename argument */
	      if (optind == ptr_child_clrequest->inargc) {
		send_status(ptr_child_clrequest->fd, 111, TERM_NO);
		retval = 1;
		goto cleanup_db;
	      }

	      retval = createdb(ptr_child_clrequest, ptr_addresult, optind);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* summary for client */
		sprintf(child_returnmsg, ULLSPEC" created:"ULLSPEC" failed\n", (unsigned long long)ptr_addresult->success, (unsigned long long)ptr_addresult->failure);
	      }
	    }

	    /*********************************************** deletedb ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "deletedb") == 0) {

	      /* test whether we have a filename argument */
	      if (optind == ptr_child_clrequest->inargc) {
		send_status(ptr_child_clrequest->fd, 111, TERM_NO);
		retval = 1;
		goto cleanup_db;
	      }

	      retval = deletedb(ptr_child_clrequest, optind, ptr_addresult);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* summary for client */
		sprintf(child_returnmsg, ULLSPEC" deleted:"ULLSPEC" failed\n", (unsigned long long)ptr_addresult->success, (unsigned long long)ptr_addresult->failure);
	      }
	    }

	    /*********************************************** confserv ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "confserv") == 0) {

	      retval = child_confserv(ptr_child_clrequest, ptr_addresult);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* summary for client */
		sprintf(child_returnmsg, ULLSPEC" submitted:"ULLSPEC" failed\n", (unsigned long long)ptr_addresult->success,(unsigned long long) ptr_addresult->failure);
	      }
	    }

	    /*********************************************** selectdb ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "selectdb") == 0) {
	      retval = listdb(ptr_child_clrequest, 1, ptr_addresult);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* summary for client */
		strcpy(child_returnmsg, "1 selected\n");
	      }
	    }

	    /*********************************************** addref ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "addref") == 0) {
	      if (!*(ptr_child_clrequest->db_encoding)) {
		strcpy(ptr_child_clrequest->db_encoding, default_input_encoding);
	      }
	      retval = addref(ptr_child_clrequest, ptr_biblio_info, set_owner, ptr_addresult, ADDREF_ADD, n_keep_id, in_format, ref_format, &id_sentinel);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		sprintf(child_returnmsg, ULLSPEC" added:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
	      }
	    }

	    /*********************************************** checkref ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "checkref") == 0) {
	      if (!*(ptr_child_clrequest->db_encoding)) {
		strcpy(ptr_child_clrequest->db_encoding, default_input_encoding);
	      }
	      retval = addref(ptr_child_clrequest, ptr_biblio_info, set_owner, ptr_addresult, ADDREF_CHECK, n_keep_id, in_format, ref_format, &id_sentinel);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		sprintf(child_returnmsg, ULLSPEC" checked:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
	      }
	    }

	    /*********************************************** updateref ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "updateref") == 0) {
	      if (!*(ptr_child_clrequest->db_encoding)) {
		strcpy(ptr_child_clrequest->db_encoding, default_input_encoding);
	      }
	      retval = addref(ptr_child_clrequest, ptr_biblio_info, set_owner, ptr_addresult, n_personal, n_keep_id, in_format, ref_format, &id_sentinel);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		
		sprintf(child_returnmsg, ULLSPEC" updated:"ULLSPEC" added:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->updated), (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
	      }
	    }

	    /*********************************************** deleteref ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "deleteref") == 0) {

	      /* test whether we have a size argument */
	      if (optind == ptr_child_clrequest->inargc) {
		iwrite(ptr_child_clrequest->fd, get_status_msg(111), strlen(get_status_msg(111)));
	      }

	      retval = deleteref(ptr_child_clrequest, ptr_addresult);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" deleted:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
	      }	      
	    }

	    /*********************************************** pickref ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "pickref") == 0) {

	      /* test whether we have a size argument */
	      if (optind == ptr_child_clrequest->inargc) {
		send_status(ptr_child_clrequest->fd, 111, TERM_NO);
		retval = 1;
		goto cleanup_db;
	      }

	      retval = pickref(ptr_child_clrequest, n_remove, ptr_addresult);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		if (n_remove) {
		  sprintf(child_returnmsg, ULLSPEC" dumped:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
		}
		else { /* if add */
		  sprintf(child_returnmsg, ULLSPEC" picked:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
		} 
	      }
	    }

	    /*********************************************** getref ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getref") == 0) {
	      retval = getref(ptr_child_clrequest, ptr_biblio_info, ref_format, n_privatelist, ptr_addresult, 1, n_frequency);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" retrieved:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->failure));
	      }	      
	    }
	    
	    /*********************************************** countref ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "countref") == 0) {
	      retval = getref(ptr_child_clrequest, ptr_biblio_info, ref_format, n_privatelist, ptr_addresult, 0, 0);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" counted\n", (unsigned long long)(ptr_addresult->success));
	      }	      
	    }
	    
	    /*********************************************** getrefx ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getrefx") == 0) {
	      retval = getrefx(ptr_child_clrequest, ptr_biblio_info, ref_format, n_privatelist, ptr_addresult);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" retrieved:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->failure));
	      }	      
	    }
	    
	    /*********************************************** addnote ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "addnote") == 0) {
	      retval = addnote(ptr_child_clrequest, set_owner, ptr_addresult, 0, &id_sentinel);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		sprintf(child_returnmsg, ULLSPEC" added:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
	      }
	    }

	    /*********************************************** updatenote ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "updatenote") == 0) {
	      retval = addnote(ptr_child_clrequest, set_owner, ptr_addresult, 1, &id_sentinel);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		sprintf(child_returnmsg, ULLSPEC" updated:"ULLSPEC" added:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->updated), (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
	      }
	    }

	    /*********************************************** deletenote ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "deletenote") == 0) {

	      /* test whether we have an argument */
	      if (optind == ptr_child_clrequest->inargc) {
		send_status(ptr_child_clrequest->fd, 111, TERM_NO);
		retval = 1;
		goto cleanup_db;
	      }

	      retval = deletenote(ptr_child_clrequest, ptr_addresult);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" deleted:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
	      }
	    }

	    /*********************************************** getnote ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getnote") == 0) {
	      retval = getnote(ptr_child_clrequest, ptr_biblio_info, ref_format, n_privatelist, ptr_addresult, 1 /* send data */);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" retrieved:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->failure));
	      }	      
	    }
	    
	    /*********************************************** countnote ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "countnote") == 0) {
	      retval = getnote(ptr_child_clrequest, ptr_biblio_info, ref_format, n_privatelist, ptr_addresult, 0 /* don't send data */);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" counted\n", (unsigned long long)(ptr_addresult->success));
	      }	      
	    }
	    
	    /*********************************************** addlink ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "addlink") == 0) {
	      retval = addlink(ptr_child_clrequest, set_owner, ptr_addresult, n_remove);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		if (n_remove) {
		  sprintf(child_returnmsg, ULLSPEC" removed:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
		}
		else {
		  sprintf(child_returnmsg, ULLSPEC" added:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
		}
	      }
	    }

	    /*********************************************** getau *****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getau") == 0) {
	      retval = getfoo(ptr_child_clrequest, GETAU, 0, ptr_child_clrequest->db_encoding, ptr_addresult, format_string);
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* let the client know what happened */
		sprintf(child_returnmsg, ULLSPEC"\n", (unsigned long long)(ptr_addresult->success));
	      }
	    }

	    /*********************************************** getax *****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getax") == 0) {
	      retval = getfoo(ptr_child_clrequest, GETAX, 0, ptr_child_clrequest->db_encoding, ptr_addresult, format_string);
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* let the client know what happened */
		sprintf(child_returnmsg, ULLSPEC"\n", (unsigned long long)(ptr_addresult->success));
	      }
	    }

	    /*********************************************** geted *****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "geted") == 0) {
	      retval = getfoo(ptr_child_clrequest, GETED, 0, ptr_child_clrequest->db_encoding, ptr_addresult, format_string);
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* let the client know what happened */
		sprintf(child_returnmsg, ULLSPEC"\n", (unsigned long long)(ptr_addresult->success));
	      }
	    }

	    /*********************************************** getas *****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getas") == 0) {
	      retval = getfoo(ptr_child_clrequest, GETAS, 0, ptr_child_clrequest->db_encoding, ptr_addresult, format_string);
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* let the client know what happened */
		sprintf(child_returnmsg, ULLSPEC"\n", (unsigned long long)(ptr_addresult->success));
	      }
	    }

	    /*********************************************** getkw *****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getkw") == 0) {
	      retval = getfoo(ptr_child_clrequest, GETKW, 0, ptr_child_clrequest->db_encoding, ptr_addresult, format_string);
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* let the client know what happened */
		sprintf(child_returnmsg, ULLSPEC"\n", (unsigned long long)(ptr_addresult->success));
	      }
	    }

	    /*********************************************** getjo *****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getjo") == 0) {
	      retval = getfoo(ptr_child_clrequest, GETJO, n_all, ptr_child_clrequest->db_encoding, ptr_addresult, format_string);
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* let the client know what happened */
		sprintf(child_returnmsg, ULLSPEC"\n", (unsigned long long)(ptr_addresult->success));
	      }
	    }

	    /*********************************************** getjf *****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getjf") == 0) {
	      retval = getfoo(ptr_child_clrequest, GETJF, n_all, ptr_child_clrequest->db_encoding, ptr_addresult, format_string);
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* let the client know what happened */
		sprintf(child_returnmsg, ULLSPEC"\n", (unsigned long long)(ptr_addresult->success));
	      }
	    }

	    /*********************************************** getj1 *****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getj1") == 0) {
	      retval = getfoo(ptr_child_clrequest, GETJ1, n_all, ptr_child_clrequest->db_encoding, ptr_addresult, format_string);
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* let the client know what happened */
		sprintf(child_returnmsg, ULLSPEC"\n", (unsigned long long)(ptr_addresult->success));
	      }
	    }

	    /*********************************************** getj2 *****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getj2") == 0) {
	      retval = getfoo(ptr_child_clrequest, GETJ2, n_all, ptr_child_clrequest->db_encoding, ptr_addresult, format_string);
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* let the client know what happened */
		sprintf(child_returnmsg, ULLSPEC"\n", (unsigned long long)(ptr_addresult->success));
	      }
	    }

	    /*********************************************** updatejo ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "updatejo") == 0) {
	      retval = updatejo(ptr_child_clrequest, ptr_addresult);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		sprintf(child_returnmsg, ULLSPEC" updated:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
	      }
	    }

	    /*********************************************** adduser ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "adduser") == 0) {
	      retval = adduser(ptr_child_clrequest, user_host, newuser_passwd, n_remove, n_readonly, ptr_addresult);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* let the client know what happened */
		if (n_remove) {
		  sprintf(child_returnmsg, ULLSPEC" removed:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->failure));
		}
		else {
		  sprintf(child_returnmsg, ULLSPEC" added:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->failure));
		}
	      }
	    }

	    /*********************************************** addword ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "addword") == 0) {
	      retval = addword(ptr_child_clrequest, n_remove, ptr_addresult);

	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);

		/* command summary */
		if (n_remove) {
		  sprintf(child_returnmsg, ULLSPEC" removed:"ULLSPEC" failed\n", (unsigned long long)ptr_addresult->success, (unsigned long long)ptr_addresult->failure);
		}
		else {
		  sprintf(child_returnmsg, ULLSPEC" added:"ULLSPEC" failed\n", (unsigned long long)ptr_addresult->success, (unsigned long long)ptr_addresult->failure);
		}
	      }
	    }

	    /*********************************************** getbib ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getbib") == 0) {

/* 	    printf("child sleeps PID=%d\n", getpid()); */
/* 	    sleep(10); */
	      retval = getbib(ptr_child_clrequest, ptr_biblio_info, ref_format, ptr_addresult);

	      if (retval != 1) {
		/* let the client know what happened, add 256 chars for
		 command summary */
		if (ptr_addresult->msg && strlen(ptr_addresult->msg)+256 > MSG_BUF_SIZE) {
		  child_returnmsg = (char*)realloc(child_returnmsg, strlen(ptr_addresult->msg)+16);
		}
		if (!child_returnmsg) {
		  /* todo: do something intelligent here */
		  tiwrite(ptr_child_clrequest->fd, get_status_msg(801), TERM_YES);
		  retval = 1;
		}
		else {
		  if (ptr_addresult->msg && *(ptr_addresult->msg)) {
		  sprintf(child_returnmsg, "getbib error: %s\n", ptr_addresult->msg);
		  }
		}

		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(&(child_returnmsg[strlen(child_returnmsg)]), ULLSPEC" retrieved:"ULLSPEC" failed:"ULLSPEC" not found\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->failure), (unsigned long long)(ptr_addresult->skipped));
	      }
	    }

	    /*********************************************** gettexbib ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "gettexbib") == 0) {
	      retval = gettexbib(ptr_child_clrequest, ptr_biblio_info, ptr_addresult);
	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" retrieved:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->failure));
	      }
	    }

	    /*********************************************** addstyle ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "addstyle") == 0) {

	      retval = addstyle(ptr_child_clrequest, ptr_addresult);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" added:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->failure));
	      }
	    }

	    /*********************************************** deletestyle ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "deletestyle") == 0) {

	      retval = deletestyle(ptr_child_clrequest, ptr_addresult);

	      /* let the client know what happened */
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" deleted:"ULLSPEC" skipped:"ULLSPEC" failed\n", (unsigned long long)(ptr_addresult->success), (unsigned long long)(ptr_addresult->skipped), (unsigned long long)(ptr_addresult->failure));
	      }	      
	    }

	    /*********************************************** getstyle ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "getstyle") == 0) {
	      retval = getstyle(ptr_child_clrequest, ptr_addresult);
	      if (retval != 1) {
		n_client_status = read_status(ptr_child_clrequest->fd);
		send_status(ptr_child_clrequest->fd, 0, TERM_NO);
		sprintf(child_returnmsg, ULLSPEC" retrieved:"ULLSPEC" failed:"ULLSPEC" not found\n", (unsigned long long)ptr_addresult->success, (unsigned long long)ptr_addresult->failure, (unsigned long long)ptr_addresult->skipped);
	      }	      
	    }

	    /*********************************************** scankw ****/
	    else if (strcmp((ptr_child_clrequest->inargv)[0], "scankw") == 0) {
	      /* we don't really do anything here. The keyword scan runs
		 after the client/server dialog is done */
	      send_status(ptr_child_clrequest->fd, 0, TERM_NO);
	      sprintf(child_returnmsg, "408:%s\n", ptr_child_clrequest->current_db);
	      tiwrite(ptr_child_clrequest->fd, child_returnmsg, TERM_YES);
	      n_client_status = read_status(ptr_child_clrequest->fd);
	      send_status(ptr_child_clrequest->fd, 0, TERM_NO);
	      strcpy(child_returnmsg, "1\n");
	    }

	    /*********************************************** unknown ****/
	    else {
	      send_status(ptr_child_clrequest->fd, 800, TERM_NO);
	      snprintf(child_returnmsg, 256, "841:%s", (ptr_child_clrequest->inargv)[0]);
	      LOG_PRINT(LOG_DEBUG, child_returnmsg);
	      snprintf(child_returnmsg, 256, "841:%s\n", (ptr_child_clrequest->inargv)[0]);
	      tiwrite(ptr_child_clrequest->fd, child_returnmsg, TERM_YES);

	      /* return error in any case */
	      retval = 1;
	    }


	    /*******************************************************/
	    /* end of client dialog                                */
	    /*******************************************************/

	  finish:
	    if (retval != 1) {
	      LOG_PRINT(LOG_DEBUG, "command processing done, finish dialog now");

	      /* finish dialog with client */
	      if (child_returnmsg) {
		if (*child_returnmsg) {
		  tiwrite(ptr_child_clrequest->fd, "999:", TERM_NO);
		}
		tiwrite(ptr_child_clrequest->fd, child_returnmsg, TERM_YES);
	      }
	      else {
		tiwrite(ptr_child_clrequest->fd, "no luck today", TERM_YES);
	      }

	      /* wait for receipt from client, reuse result for client status */
	      if ((result = read_status(ptr_child_clrequest->fd))) {
		LOG_PRINT(LOG_WARNING, get_status_msg(result));
	      }

	      /* run post-dialog code here. All of this stuff
		 will be invisible to the client */
	      if ((!strcmp((ptr_child_clrequest->inargv)[0], "addref")
		   || !strcmp((ptr_child_clrequest->inargv)[0], "updateref"))
		  && keyword_scan[0] == 't') {
		run_keyword_scan(ptr_child_clrequest, &id_sentinel, 0);
	      }
	      else if ((!strcmp((ptr_child_clrequest->inargv)[0], "addnote")
			|| !strcmp((ptr_child_clrequest->inargv)[0], "updatenote"))
		       && keyword_scan[0] == 't') {
		run_keyword_scan(ptr_child_clrequest, &id_sentinel, 1);
	      }
	      else if (!strcmp((ptr_child_clrequest->inargv)[0], "scankw")) {
		run_keyword_scan(ptr_child_clrequest, NULL, 2);
	      }

	      delete_all_lilid(&id_sentinel);
	    }

	  cleanup_db:
	    /* clean up database abstraction layer */
	    if (n_dbi_is_up) {
	      my_dbi_shutdown(ptr_child_clrequest->inst);
	    }

	    /* function checks ptr_dbcaps == NULL */
	    free_db_caps(ptr_dbcaps);

	  cleanup:
	    if (ptr_child_clrequest->inargv) {
	      free(ptr_child_clrequest->inargv);
	    }
	    if (child_inbuffer) {
	      free(child_inbuffer);
	    }
	    if (child_inbuffer1) {
	      free(child_inbuffer1);
	    }
	    if (child_returnmsg) {
	      free(child_returnmsg);
	    }

	    free_addresult(ptr_addresult);
	    free(ptr_biblio_info);

	    if (retval == 1) {
	      /* take a break to let the clients finish reading the error code */
	      sleep(1);
	    }

	    close(ptr_child_clrequest->fd);
	    FD_CLR(ptr_child_clrequest->fd, &readfds);
	    sprintf(child_msg_buffer, "child finished client on fd %d", ptr_child_clrequest->fd);
	    LOG_PRINT(LOG_INFO, child_msg_buffer);
	    free(child_msg_buffer);

	    /* write log data to disk */
	    if (fp_log_file != NULL) {
	      fflush(fp_log_file);
	    }

	    free_client_request(ptr_child_clrequest);

	    if (!n_nofork) {
	      if (fd_fifo != -1) {
		close(fd_fifo);
	      }

	      if (n_log_dest == 1) {
		closelog();
	      }
	      exit (retval); /* child is done and leaves stage */
	    }
	    else { /* didn't fork, clean up "parent" stuff */
 	      delete_olili(&first_olili, client_sockfd);

	      /* the next child will know it is not the first one */
	      is_first_child = 0;
	    }
	  }
	  else { /* we're parent, close client connection */
	    close(ptr_clrequest->fd);
	    FD_CLR(ptr_clrequest->fd, &readfds);
	    sprintf(msg_buffer, "parent removing client on fd %d", ptr_clrequest->fd);
	    LOG_PRINT(LOG_INFO, msg_buffer);

	    /* decrease n_max_fd to minimize loops */
	    delete_olili(&first_olili, ptr_clrequest->fd);

	    /* the next child will know it is not the first one */
	    is_first_child = 0;

	  } /* end if fork() */
	} /* end: if (fd == server_sockfd) */
      } /* end: if (FD_ISSET(fd, &testfds)) */
    } /* end: for() */
  } /* end: while(run) */
  LOG_PRINT(LOG_INFO, "server exited gracefully");
  free(fifo_buffer);
  if (fp_log_file != NULL) {
    fclose(fp_log_file); /* not strictly necessary here, but it's nicer */
  }
  if (fd_fifo != -1) {
    close(fd_fifo);
    unlink(the_fifo);
  }
  free_client_request(ptr_clrequest);

  /* duh */
  exit (0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  sig_handler(): handler for signals that are supposed to stop the
                 server
  static void sig_handler has no return value

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void sig_handler (int sig) {
  run = 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  sighup_handler(): handler for signals that are supposed to reread
                 the config file
  static void sighup_handler has no return value

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void sighup_handler (int sig) {
  gotsignal = 1;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  sigchld_handler(): handler for signals that tell us a child is done
                 
  static void sigchld_handler has no return value

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void sigchld_handler(int sig) {
  int status;

  gotchldsig = 1;

  if (waitpid(-1, &status, WNOHANG) >= 0) { /* non-blocking wait */
    if (WIFEXITED(status)) {
      child_exitval = WEXITSTATUS(status); /* set global for logging */
    }
  }
  /* else: ignore failure */
}

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

  void log_print

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

  const char* string a string containing the message to be logged

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void log_print(int priority, const char* string) {
  /* we must have this fn in refdbd.c because FILE* fp_log_file
     cannot be declared extern (??) */
  time_t the_time;
  char timestring[256];
  FILE* new_fp_log_file;

  if (n_log_dest == 0) { /* output on stderr */
    fprintf(stderr, "%s\n", string);
  }
  else if (n_log_dest == 1) { /* output via syslog */
    if (n_reopen_log) {
      openlog("refdbd", LOG_PID|LOG_ODELAY, LOG_USER);
      n_reopen_log = 0;
      LOG_PRINT(LOG_INFO, "reopened log file");
    }
    syslog(priority, "%s", string);
  }
  else { /* output in user-defined logfile */
    if (n_reopen_log) { /* log filename or destination has changed*/
      n_reopen_log = 0; /* reset switch; must be done before we recursively
			 call LOG_PRINT */
      if ((new_fp_log_file = fopen(log_file, "ab")) == NULL) {
	n_log_dest = 1; /* fall back to syslog */
	openlog("refdbd", LOG_PID|LOG_ODELAY, LOG_USER);
	fclose(fp_log_file);
	LOG_PRINT(LOG_WARNING, "could not open custom log file");
      }
      else {
	fclose(fp_log_file);
	fp_log_file = new_fp_log_file;
	LOG_PRINT(LOG_INFO, "reopened log file");
      }
    }
    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);
    /* use this only to debug crashes */
    /* fflush(fp_log_file); */
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  new_client_request(): creates a CLIENT_REQUEST structure

  struct CLIENT_REQUEST* new_client_request returns the new structure
                        or NULL if there was an error

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct CLIENT_REQUEST* new_client_request(void) {
  struct CLIENT_REQUEST* ptr_clrequest;

  if ((ptr_clrequest = malloc(sizeof(struct CLIENT_REQUEST))) == NULL) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return NULL;
  }
  else {
    initialize_client_request(ptr_clrequest);
    return ptr_clrequest;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  initialize_client_request(): initializes a CLIENT_REQUEST structure

  int initialize_client_request returns 0

  struct CLIENT_REQUEST* ptr_clrequest ptr to an existing CLIENT_REQUEST
                         structure

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int initialize_client_request(struct CLIENT_REQUEST* ptr_clrequest) {
  ptr_clrequest->fd = 0;
  ptr_clrequest->n_cgi = 0;
  ptr_clrequest->inargc = 0;
  ptr_clrequest->inargv = NULL;
  ptr_clrequest->username[0] = '\0';
  ptr_clrequest->passwd[0] = '\0';
  strcpy(ptr_clrequest->server_ip, "localhost");
  strcpy(ptr_clrequest->dbs_port_address, "3306");
  strcpy(ptr_clrequest->dbserver, "sqlite3"); /* most likely default engine */
  ptr_clrequest->current_db[0] = '\0';
  ptr_clrequest->db_path[0] = '\0';
  ptr_clrequest->pdfroot[0] = '\0';
  ptr_clrequest->cgi_url[0] = '\0';
  ptr_clrequest->limit[0] = '\0';
  ptr_clrequest->inst = NULL;
  
  /* always return success */
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  dup_client_request(): duplicates a CLIENT_REQUEST structure

  struct CLIENT_REQUEST*  dup_client_request returns the duplicate or
                        NULL if there was an error

  struct CLIENT_REQUEST* ptr_clrequest ptr to an existing CLIENT_REQUEST
                         structure

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct CLIENT_REQUEST* dup_client_request(struct CLIENT_REQUEST* ptr_clrequest) {
  struct CLIENT_REQUEST* ptr_new_clrequest;

  if (!ptr_clrequest) {
    return NULL;
  }

  if ((ptr_new_clrequest = malloc(sizeof(struct CLIENT_REQUEST))) == NULL) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return NULL;
  }

  /* start with an identical copy */
  memcpy((void*)ptr_new_clrequest, (const void*)ptr_clrequest, sizeof(struct CLIENT_REQUEST));

  /* initialize client-provided values */
  ptr_new_clrequest->n_cgi = 0;
  ptr_new_clrequest->inargc = 0;
  ptr_new_clrequest->inargv = NULL;
  ptr_new_clrequest->username[0] = '\0';
  ptr_new_clrequest->passwd[0] = '\0';

  return ptr_new_clrequest;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  free_client_request(): frees a CLIENT_REQUEST structure

  int initialize_client_request returns 0 if ok, 1 if invalid ptr

  struct CLIENT_REQUEST* ptr_clrequest ptr to an existing CLIENT_REQUEST
                         structure

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int free_client_request(struct CLIENT_REQUEST* ptr_clrequest) {
  if (ptr_clrequest) {
    free(ptr_clrequest);
    return 0;
  }
  else {
    return 1;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  new_addresult(): creates and initializes an ADDRESULT structure

  struct ADDRESULT* new_addresult returns a pointer to a structure
                    if ok, NULL if failed

  size_t msg_len length of the message buffer. The function will allocate
                 and initialize a string buffer of the requested length

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct ADDRESULT* new_addresult(size_t msg_len) {
  struct ADDRESULT* ptr_addresult;

  if ((ptr_addresult = malloc(sizeof(struct ADDRESULT))) == NULL) {
    return NULL;
  }

  ptr_addresult->success = 0;
  ptr_addresult->failure = 0;
  ptr_addresult->updated = 0;
  ptr_addresult->skipped = 0;
  ptr_addresult->msg_len = msg_len;
  if (msg_len == 0) {
    ptr_addresult->msg = NULL;
  }
  else {
    if ((ptr_addresult->msg = malloc(msg_len)) == NULL) {
      free(ptr_addresult);
      return NULL;
    }
    *(ptr_addresult->msg) = '\0'; /* turn buffer into empty string */
  }

  return ptr_addresult;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  free_addresult(): frees the memory allocated for an ADDRESULT structure

  void free_addresult 

  struct ADDRESULT* ptr_addresult ptr to the structure to be freed

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void free_addresult(struct ADDRESULT* ptr_addresult) {
  if (ptr_addresult) {
    if (ptr_addresult->msg) {
      free(ptr_addresult->msg);
    }
    free(ptr_addresult);
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  new_bibinfo(): creates and initializes a bibinfo structure

  struct bibinfo* new_bibinfo returns a pointer to a structure
                    if ok, NULL if failed

  char* format_string ptr to a string containing format info

  char* sort_string ptr to a string containing sort info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct bibinfo* new_bibinfo(char* format_string, char* sort_string) {
  struct bibinfo* ptr_biblio_info;

  if ((ptr_biblio_info = malloc(sizeof(struct bibinfo))) == NULL) {
    return NULL;
  }

  ptr_biblio_info->format_string = format_string;
  ptr_biblio_info->sort_string = sort_string;
  ptr_biblio_info->xreflabel = NULL;
  ptr_biblio_info->entry_id = NULL;
  ptr_biblio_info->is_subseq = 0;
  ptr_biblio_info->n_refdb_id = 0;
  ptr_biblio_info->year_unique_suffix = NULL;
  ptr_biblio_info->n_startnumber = 1;

  return ptr_biblio_info;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  free_bibinfo(): frees the memory allocated for a bibinfo structure

  void free_bibinfo 

  struct bibinfo* ptr_biblioinfo ptr to the structure to be freed

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void free_bibinfo(struct bibinfo* ptr_biblio_info) {
  if (ptr_biblio_info) {
    free(ptr_biblio_info);
  }
}


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

  int postprocess_var returns 0 if ok, 1 if error

  char* varname name of the variable to check

  char* string ptr to the string to modify or check

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int postprocess_var(char* varname, char* string) {

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

  /* individual treatment for each variable */
  if (!strcmp(varname, "passwd")) {
    if (strcmp(string, "*") == 0) {
      ask_for_passwd(string);
    }
    return 0;
  }
  else if (!strcmp(varname, "username")) {
    if (!*string && getlogin()) {
      strcpy(string, getlogin()); /* although not recommended, the login name
				     is a good guess */
    }
    else if (!*string) {
      /* provide a dummy name */
      strcpy(string, "root");
    }
    return 0;
  }
  return 1; /* should never happen */
}



