/*+++++++++++++++++
  refdbib - the refdb bibliography client console application
  markus@mhoenicka.de 9-25-00
  $Id: refdbib.c,v 1.41.2.11 2006/02/13 21:33:54 mhoenicka Exp $

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, see <http://www.gnu.org/licenses/>

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



/* general includes */
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h> 
#include <limits.h>
#include <syslog.h>
#include <signal.h>
#include "getopt.h"

/* our own stuff */
#include "refdb.h"  /* common stuff for all refdb applications*/
#include "connect.h"
#include "linklist.h" /* for linked lists */
#include "pref.h" /* for init file, depends on linklist.h */
#include "strfncs.h" /* for is functions, LOG_PRINT definition */
#include "readln.h"  /* readline-related  stuff, need only for command dummy */
#include "page.h" /* pager functions */
#include "refdb-client.h" /* stuff common to all clients */
#include "refdbib.h" /* stuff specific to this client */
#include "readris.h" /* fncs to access RIS files */
#include "tokenize.h" /* cuts command line into tokens */
#include "passwd.h" /* securely obtain passwords */
#include "outformats.h" /* definitions of output formats */

/* Globals */

char current_db[PREFS_BUF_LEN] = "";

/*+ this array will hold the user preferences +*/
Prefs prefs[22] = {
  {"serverip", ""},
  {"port", ""},
  {"verbose", ""},
  {"pager", ""},
  {"username", ""},
  {"passwd", ""},
  {"defaultdb", ""},
  {"timeout", ""},
  {"outtype", ""},
  {"outformat", ""},
  {"logfile", ""},
  {"logdest", ""},
  {"loglevel", ""},
  {"stylespecdir", ""},
  {"refdblib", ""},
  {"startnumber", ""},
  {"toencoding", ""},
  {"ignore_missing", ""},
  {"raw", ""},
  {"pdfroot", ""},
  {"no_encrypt", ""},
  {"", ""}
};

/* these are the configurable variables with the compile-time defaults */
char server_ip[PREFS_BUF_LEN] = "127.0.0.1"; /*+ default IP address of refdbd +*/
char port_address[PREFS_BUF_LEN] = "9734"; /*+ default port address of refdbd +*/
char the_pager[PREFS_BUF_LEN] = "stdout"; /*+ default pager +*/
char username[PREFS_BUF_LEN] = ""; /*+ default username (emtpy) +*/
char passwd[PREFS_BUF_LEN] = "*"; /*+ default password (ask user) +*/
char refdb_timeout[PREFS_BUF_LEN] = "180"; /* 180 seconds default timeout */
char output_type[PREFS_BUF_LEN] = "db31"; /* docbook is default output fmt */
char output_format[PREFS_BUF_LEN] = ""; /* format specification */
char log_file[PREFS_BUF_LEN] = "/var/log/refdbib.log"; /*+ default log file +*/
char log_dest[PREFS_BUF_LEN] = "1"; /*+ default log destination (0 = stderr, 1 = syslog, 2 = log_file +*/
char log_level[PREFS_BUF_LEN] = "6"; /*+ default level up to which messages are logged (0 through 7). -1 means no logging +*/
char stylespec_dir[PREFS_BUF_LEN] = "."; /* use PWD for stylespec files */
char refdblib[PREFS_BUF_LEN] = ""; /* path to shareable files */
char verbose[PREFS_BUF_LEN] = ""; /* verbose output? */
char startnumber[PREFS_BUF_LEN] = ""; /* first number of reference */
char encoding[PREFS_BUF_LEN] = ""; /* output encoding */
char ignore_missing[PREFS_BUF_LEN] = "f"; /* ignore missing refs */
char confdir[_POSIX_PATH_MAX+1] = ""; /* path to the config files */
char namespace[PREFS_BUF_LEN] = ""; /* namespace for XML output */
char raw[PREFS_BUF_LEN] = ""; /* if t, retrieve raw instead of cooked bibliography */
char pdfroot[PREFS_BUF_LEN] = ""; /* root of path to offprints */
char no_encrypt[PREFS_BUF_LEN] = ""; /* do not encrypt passwords if 't' */

int n_refdb_timeout;
int n_broken_pipe; /*+ 1 indicates that we attempted to write to a broken pipe +*/
int n_abort_connect; /*+ 1 indicates that we want to abort a connection +*/
int n_log_dest = 1; /* destination of log output */
int n_log_level = 0; /* level of log information that will be printed */
int n_read_stdin = 0; /* if 1, data try to squeeze in at stdin */
int n_exit_code = 0; /* any other value than 0 indicates an error */
int n_cgi = 0; /* if 1, we run as a cgi app */
int n_verbose = 0; /* verbose output if 1 */
FILE* fp_log_file = NULL; /* ptr to logfile structure */

extern const char cs_term[];

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

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  The one and only main function
  
  int main returns 0 if successful, 1 if error
  
  int argc number of arguments

  char** argv ptr to array of strings with the command line arguments
  
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int main (int argc, char** argv)
{
  char *processed_infile;
  char msgbuf[512]; /* buffer to assemble error messages */
  char infile[_POSIX_PATH_MAX];
  int n_opt, i;
  int j = 0;
  int n_readinit = 1; /* if 1, read config file. If 0, skip config file */
  int n_type = 0; /* output type, numerical version
		     0 = db31
		     1 = db31x
		     2 = teix
		     3 = bibtex */

  FILE* fp_infile = NULL;
  struct sigaction act, oldact, intact, oldintact;

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

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

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

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

  /* initialize the array of preference values */
  prefs[0].varvalue = server_ip;
  prefs[1].varvalue = port_address;
  prefs[2].varvalue = verbose;
  prefs[3].varvalue = the_pager;
  prefs[4].varvalue = username;
  prefs[5].varvalue = passwd;
  prefs[6].varvalue = current_db;
  prefs[7].varvalue = refdb_timeout;
  prefs[8].varvalue = output_type;
  prefs[9].varvalue = output_format;
  prefs[10].varvalue = log_file;
  prefs[11].varvalue = log_dest;
  prefs[12].varvalue = log_level;
  prefs[13].varvalue = stylespec_dir;
  prefs[14].varvalue = refdblib;
  prefs[15].varvalue = startnumber;
  prefs[16].varvalue = encoding;
  prefs[17].varvalue = ignore_missing;
  prefs[18].varvalue = raw;
  prefs[19].varvalue = pdfroot;
  prefs[20].varvalue = no_encrypt;

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

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

  /* read command line settings. These may override the config file settings */
  while ((n_opt = getopt(argc, argv, "c:d:D:e:E:f:hi:l:L:mn:N:p:qrS:t:T:u:vVw:xy:")) != -1) {
    switch (n_opt) {
    case 'c':
      strncpy(the_pager, optarg, PREFS_BUF_LEN);
      the_pager[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'd':
      strncpy(current_db, optarg, PREFS_BUF_LEN);
      current_db[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'D':
      strncpy(stylespec_dir, optarg, PREFS_BUF_LEN);
      stylespec_dir[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(encoding, optarg, PREFS_BUF_LEN);
      encoding[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'f':
      if (!strcmp(optarg, "stdin")) {
	n_read_stdin = 1;
      }
      break;
    case 'h':
      fprintf(stderr, "Create a bibliography file based on an ID or citation key list\nThe list must be provided as an XML document using the citationlistx DTD either on stdin or by passing the file\'s path as an argument\nUsage: refdbib [-c pager ] [-d db] [-D dir] [-e logdest] [-E encoding] [-f stdin] [-h] [-i address] [-l log-level] [-L logfile] [-m] [-n namespace-prefix] [-N number] [-p port] [-q] [-r][-S style] [-t type] [-T time] [-u name] [-v] [-V] [-w password] [-x] [-y confdir] file\nOptions: -c pager command\n         -d use database db\n         -D save driver files in dir\n         -e log destination (0=stderr, 1=syslog, 2=custom file)\n         -E encoding set the output character encoding\n         -f stdin read data from stdin (kludge only required for Cygwin)\n         -h prints this help\n         -i set server IP address\n         -l set the log level\n         -L set the log file\n         -m ignore missing references\n         -n set optional namespace prefix for XML output\n         -N start numbering bibliography entries with number\n         -p set server port\n         -q ignore init-file\n         -r retrieve raw rather than cooked bibliography\n         -S use bibliography style\n         -t output type (db31|db31x|db50x|teix|tei5x|bibtex|rtf)\n         -T set timeout in seconds\n         -u set username\n         -v show version information\n         -V switch to verbose mode\n         -w set password\n         -x do not encrypt passwords\n         -y look for configuration files in confdir\n");
      exit (0);
      break;
    case 'i':
      strncpy(server_ip, optarg, PREFS_BUF_LEN);
      server_ip[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'l':
      strncpy(log_level, optarg, PREFS_BUF_LEN);
      log_level[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'L':
      strncpy(log_file, optarg, PREFS_BUF_LEN);
      log_file[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'm':
      *ignore_missing = 't';
      break;
    case 'n':
      strncpy(namespace, optarg, PREFS_BUF_LEN);
      namespace[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'N':
      strncpy(startnumber, optarg, PREFS_BUF_LEN);
      break;
    case 'p':
      strncpy(port_address, optarg, PREFS_BUF_LEN);
      port_address[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'q':
      n_readinit = 0;
      break;
    case 'r':
      strcpy(raw, "t");
      break;
    case 'S':
      strncpy(output_format, optarg, PREFS_BUF_LEN);
      output_format[PREFS_BUF_LEN-1] = '\0';
      break;
    case 't':
      strncpy(output_type, optarg, PREFS_BUF_LEN);
      output_type[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'T':
      strncpy(refdb_timeout, optarg, PREFS_BUF_LEN);
      refdb_timeout[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'u':
      strncpy(username, optarg, PREFS_BUF_LEN);
      username[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'v':
      printf("refdbib %s built from svn revision %s markus@mhoenicka.de\nYou may redistribute and modify this software under the terms of the GNU General Public License.\n", VERSION, svn_version());
      exit (0);
      break;
    case 'V':
      strcpy(verbose, "t");
      break;
    case 'w':
      strncpy(passwd, optarg, PREFS_BUF_LEN);
      passwd[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'x':
      strcpy(no_encrypt, "t");
      break;
    case 'y':
      /* do nothing, this option is used before getopt runs */
      break;
    case ':':
      fprintf(stderr, "Usage: refdbib [-c pager ] [-d db] [-D dir] [-e logdest] [-E encoding] [-f stdin] [-h] [-i address] [-l log-level] [-L logfile] [-m] [-n namespace-prefix] [-N number] [-p port] [-q] [-r] [-S style] [-t type] [-T time] [-u name] [-v] [-V] [-w password] [-x] [-y confdir] file\nOptions: -c pager command\n         -d use database db\n         -D save driver files in dir\n         -e log destination (0=stderr, 1=syslog, 2=custom file)\n         -E encoding set the output character encoding\n         -f stdin read data from stdin (kludge only required for Cygwin)\n         -h prints this help\n         -i set server IP address\n         -l set the log level\n         -L set the log file\n         -m ignore missing references\n         -n set optional namespace prefix for XML output\n         -N start numbering bibliography entries with number\n         -p set server port\n         -q ignore init-file\n         -r retrieve raw rather than cooked bibliography\n         -S use bibliography style\n         -t output type (db31|db31x|db50x|teix|tei5x|bibtex|rtf)\n         -T set timeout in seconds\n         -u set username\n         -v show version information\n         -V switch to verbose mode\n         -w set password\n         -x do not encrypt passwords\n         -y look for configuration files in confdir\n");
      exit (1);
      break;
    case '?':
      fprintf(stderr, "unknown option %c: use refdbib -h to display usage\n", optopt);
      break;
    }
  }

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

  /* post-process configuration variables */

  if (*verbose == 't') {
    n_verbose = 1;
  }

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

  if (!is_port(port_address)) {
    fprintf(stderr, "option -p needs a value: %s is no valid port\n", optarg);
    if (*verbose == 't') {
      fprintf(stderr, "Port addresses below 1024 are reserved for system use. refdb should use a port higher than 1024. The server and all clients must use the same port.\n");
    }
    exit (1);
  }

  /* see whether we have a username */
  if (!username[0] && getlogin()) {
    strcpy(username, getlogin()); /* although not recommended, the login name
				     is a good guess */
  }
  else if (!username[0]) {
    fprintf(stderr, "missing username\n");
    exit (1);
  }

  /* see whether we need to ask for a password */
  if (strcmp(passwd, "*") == 0) {
    ask_for_passwd(passwd);
  }

  /* make some conversions */
  n_log_level = num_loglevel(log_level);
  n_log_dest = num_logdest(log_dest);
  n_refdb_timeout = atoi(refdb_timeout);

  /* fill in stylespec_dir if not explicitly set */
  if (!strcmp(stylespec_dir, ".") || !*stylespec_dir) {
    /* use PWD if the path is a single dot or if nothing else helps */
    stylespec_dir[0] = '\0'; 
  }
  else if (!strcmp(stylespec_dir, ".")) {
    stylespec_dir[0] = '\0'; /* use PWD if the path is a single dot */
  }

  if (!strcmp(output_type, "db31")) {
    n_type = REFDOCBK;
  }
  else if (!strcmp(output_type, "db31x")) {
    n_type = REFDOCBKX;
  }
  else if (!strcmp(output_type, "teix")) {
    n_type = REFTEIX;
  }
  else if (!strcmp(output_type, "bibtex")) {
    n_type = REFBIBTEX;
  }
  else if (!strcmp(output_type, "db50x")) {
    n_type = REFDOCBKX5;
  }
  else if (!strcmp(output_type, "tei5x")) {
    n_type = REFTEIX5;
  }
  else if (!strcmp(output_type, "rtf")) {
    n_type = REFRTF;
    *raw = 'f'; /* force cooked bib */
  }
  else {
    n_type = REFDOCBK; /* temporary default, may change or be removed */
  }

/*   fprintf(stderr, "username: %s; password: %s\n", username, passwd); */
  
  /* 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("refdbib", 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("refdbib", LOG_PID|LOG_ODELAY, LOG_USER);
  }

  LOG_PRINT(LOG_INFO, "refdbib started");

  /* some checks */
  if (*current_db == '\0') {
    LOG_PRINT(LOG_CRIT, "no database specified");
    n_exit_code = 127;
  }
  else {
    if (n_read_stdin || optind == argc) {
      n_read_stdin = 1; /* not set if optind == inargc */
      if (n_type == REFBIBTEX) {
	n_exit_code = make_texbib(stdin);
      }
      else { /* 0 is default */
	if (*raw != 't') {
	  n_exit_code = make_cookedbib(stdin, n_type);
	}
	else {
	  n_exit_code = make_rawbib(stdin, n_type);
	}
      }
    }
    else if (argv[optind] && *(argv[optind])) { /* read from file */
      strncpy(infile, argv[optind], (size_t)_POSIX_PATH_MAX-1);
      infile[_POSIX_PATH_MAX-1] = '\0'; /* terminate just in case */

      processed_infile = canonicalize_path(infile);
      if (processed_infile == NULL) {
	if (n_type == REFBIBTEX) {
	  strcat(infile, ".aux");
	  processed_infile = canonicalize_path(infile);
	  if (processed_infile == NULL) {
	    sprintf(msgbuf, "could not access input file %s", argv[optind]);
	    LOG_PRINT(LOG_WARNING, msgbuf);
	    n_exit_code = 1;
	    goto the_end;
	  }
	}
	else {
	  sprintf(msgbuf, "could not access input file %s", argv[optind]);
	  LOG_PRINT(LOG_WARNING, msgbuf);
	  n_exit_code = 1;
	  goto the_end;
	}
      }
      strcpy(infile, processed_infile);
      free(processed_infile);

      if ((fp_infile = fopen(infile, "rb")) == NULL) {
	sprintf(msgbuf, "could not open input file %s", argv[optind]);
	LOG_PRINT(LOG_WARNING, msgbuf);
	n_exit_code = 1;
      }
      else {
	if (n_type == REFBIBTEX) {
	  n_exit_code = make_texbib(fp_infile);
	}
	else { /* 0 is default */
	  if (*raw != 't') {
	    n_exit_code = make_cookedbib(fp_infile, n_type);
	  }
	  else {
	    n_exit_code = make_rawbib(fp_infile, n_type);
	  }
	}
	fclose(fp_infile);
      }
    }
    /* else: no file specified, do nothing */
  }
    
 the_end:
  if (n_exit_code) {
    LOG_PRINT(LOG_INFO, "refdbib finished with errors");
  }
  else {
    LOG_PRINT(LOG_INFO, "refdbib finished");
  }
  
  if (fp_log_file) {
    fclose(fp_log_file); /* not strictly necessary, but more polite */
  }
  exit (n_exit_code);
}


/* **************************************************************** */
/*                                                                  */
/*                   refdbib functions                              */
/*                                                                  */
/* **************************************************************** */

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  make_cookedbib() requests a cooked bibliography

  int make_cookedbib returns 0 if ok, 1 if error

  FILE *fp_infile pointer to a stream to read the id data from

  int n_type type of requested output, see REFXX definitions
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int make_cookedbib(FILE* fp_infile, int n_type) {
  char inbuffer[COMMAND_INBUF_LEN];
  char srv_inbuffer[COMMAND_INBUF_LEN] = "";
  char specfile[_POSIX_PATH_MAX];
  char scrambled_passwd[PASSWD_LENGTH*3+1];
  size_t outbuf_len = COMMAND_INBUF_LEN;
  int numbyte;
  int n_read_done = 0;
  int n_result;
  int n_error;
  int n_retval = 0;
  int n_curr_trailing_z = 0;
  int n_last_trailing_z = 0;
  int n_done_refs;
  int cs_status;
  size_t byte_written = 0;
  FILE *pagerfp;
  FILE *specfilefp;
  struct linger mylinger;
  struct simplelistvals slvals;

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

  slvals.outbuffer = malloc(outbuf_len);
  if (slvals.outbuffer == NULL) {
    return 1;
  }

  strcpy(slvals.outbuffer, "getbib ");

  if (connect_to_server(&slvals.n_sockfd, server_ip, port_address) != 0) {
    LOG_PRINT(LOG_ERR, "could not connect to server");
    free(slvals.outbuffer);
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(slvals.n_sockfd, scrambled_passwd, srv_inbuffer)) {
    LOG_PRINT(LOG_ERR, "server authentication failed");
    free(slvals.outbuffer);
    close(slvals.n_sockfd);
    return 1;
  }

  /* assemble command */
  switch (n_type) {
  case REFTEIX:
    strcat(slvals.outbuffer, " -t teix -u ");
    break;
  case REFDOCBKX:
    strcat(slvals.outbuffer, " -t db31x -u ");
    break;
  case REFDOCBKX5:
    strcat(slvals.outbuffer, " -t db50x -u ");
    break;
  case REFTEIX5:
    strcat(slvals.outbuffer, " -t tei5x -u ");
    break;
  case REFRTF:
    strcat(slvals.outbuffer, " -t rtf -u ");
    break;
  case REFDOCBK:
    /* fall through */
  default:
    strcat(slvals.outbuffer, " -t db31 -u ");
    break;
  }

  strcat(slvals.outbuffer, username);

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

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

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

  if (*startnumber) {
    strcat(slvals.outbuffer, " -o ");
    strcat(slvals.outbuffer, startnumber);
  }

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

  if (*namespace) {
    strcat(slvals.outbuffer, " -n \"");
    strcat(slvals.outbuffer, namespace);
    strcat(slvals.outbuffer, "\"");
  }

  /* now send the command to the application server */
/*      printf("%s\n", slvals.outbuffer); */
/*    free(slvals.outbuffer); */
/*    return 0; */

  LOG_PRINT(LOG_DEBUG, slvals.outbuffer);
  send_status(slvals.n_sockfd, 0, TERM_NO);
  numbyte = tiwrite(slvals.n_sockfd, slvals.outbuffer, TERM_YES);
  if (numbyte == -1) {
    free(slvals.outbuffer);
    fprintf(stderr, "%s\n", get_status_msg(110));
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    close(slvals.n_sockfd);
    return 1;
  }

  cs_status = read_status(slvals.n_sockfd);

  if (cs_status && cs_status != 402) {
    free(slvals.outbuffer);
    fprintf(stderr, "%s\n", get_status_msg(cs_status));
    LOG_PRINT(LOG_WARNING, get_status_msg(cs_status));
    close(slvals.n_sockfd);
    return 1;
  }

  if (cs_status == 402) {
    if (stylespec_dir && *stylespec_dir) {
      strcpy(specfile, stylespec_dir);
      if (specfile[0] && specfile[strlen(specfile)-1] != '/') {
	strcat(specfile, "/");
      }
    }
    else {
      specfile[0] = '\0';
    }
    strcat(specfile, output_format);
    if (specfile[strlen(specfile)-1] != '.') {
      strcat(specfile, ".");
    }
    if (n_type == REFDOCBK) {
      strcat(specfile, "dsl"); 
    }
    else {
      strcat(specfile, "xsl"); 
    }

    /* this is guaranteed to return a file ptr. This may be stdout
       if nothing else helps */
    specfilefp = open_outfile(specfile, 0);
/*      printf("specfile: %s<<\n", specfile); */

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

    if (n_error) {
      free(slvals.outbuffer);
      close_outfile(specfilefp);
      close(slvals.n_sockfd);
      n_broken_pipe = 0;
      return 1;
    }

    close_outfile(specfilefp);

    cs_status = read_status(slvals.n_sockfd);

    if (cs_status) {
      fprintf(stderr, "%s\n", get_status_msg(cs_status));
      LOG_PRINT(LOG_WARNING, get_status_msg(cs_status));
      free(slvals.outbuffer);
      close(slvals.n_sockfd);
      return 1;
    }
  } /* end if (n_send_stylespec) */

  byte_written = 0;

  /* run phases 1 through 4 of client/server protocol */
  n_result = send_xml_data(fp_infile, /*pagerfp*/stderr, stderr, slvals.n_sockfd, &byte_written);

  if (n_result) {
    free(slvals.outbuffer);
    close(slvals.n_sockfd);
    return 1;
  }

  /* ------------------------------------------------------------ */
  /* PHASE 5 */
  /*  signal server that we're done */

  send_status(slvals.n_sockfd, 402, TERM_NO);

  /* check server status, should be "chunk added successfully" */
  if ((cs_status = read_status(slvals.n_sockfd)) != 403) {
    if (cs_status == 400) {
      size_t byte_written_to_inbuffer;
      int num_trailz;
      int n_read_done = 0;
      /* retrieve server-generated error message */
      do {
	numbyte = tread(slvals.n_sockfd, inbuffer, OUTBUF_LEN);
	/*   	printf("phase4 server reply:%s<<\n", inbuffer); */
	if (numbyte == -1) {
	  /* timeout while reading */
	  fprintf(stderr, "%s\n", get_status_msg(109));
	  return 1;
	}
	
	/* we rely on the fact that the server reply is no more than
	   OUTBUF_LEN in length */
	if ((num_trailz = get_trailz(inbuffer, numbyte)) >= TERM_LEN) { /* if transmission ends */
	  n_read_done++;
	}
	/* write numbyte chars to output, unless this is the last chunk:
	   we do not want to write the terminating \0 */
	if (!n_broken_pipe) {
	  byte_written_to_inbuffer = fwrite(inbuffer, sizeof(char), numbyte-num_trailz, /*pagerfp*/stderr);
	}
	/* 	printf("%s", inbuffer); */
      } while (!n_read_done);
    }
    else {
      fprintf(stderr, "%s\n", get_status_msg(cs_status));
      return 1;
    }
  } /* end if cs_status */

  /* request data */
  send_status(slvals.n_sockfd, 0, TERM_NO);

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

  n_read_done = 0;
  n_last_trailing_z = 0;
  n_curr_trailing_z = 0;


  n_done_refs = 0;

  do {  /* read in loop over all chunks of data the server is going to send */

    if ((cs_status = read_status(slvals.n_sockfd)) == 402) {
      n_done_refs++;
    }
    else if (cs_status != 404) {
      fprintf(stderr, "%s\n", get_status_msg(cs_status));
      LOG_PRINT(LOG_WARNING, get_status_msg(cs_status));
      free(slvals.outbuffer);
      close(slvals.n_sockfd);
      closepager(pagerfp);
      n_broken_pipe = 0;
      return 1;
    }

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

    if (n_error) {
      closepager(pagerfp);
      n_broken_pipe = 0;
      return 1;
    }
  } while (!n_done_refs);

  /* request summary */
  send_status(slvals.n_sockfd, 0, TERM_NO);

/*    printf("byte written:%d\n", byte_written); */
/*    fflush(stdout); */


  if ((cs_status = read_status(slvals.n_sockfd)) != 0) {
    fprintf(stderr, "%s\n", get_status_msg(109));
    LOG_PRINT(LOG_WARNING, get_status_msg(109));
    free(slvals.outbuffer);
    close(slvals.n_sockfd);
    closepager(pagerfp);
    n_broken_pipe = 0;
    return 1;
  }

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

  if (n_error) {
    free(slvals.outbuffer);
    close(slvals.n_sockfd);
    closepager(pagerfp);
    n_broken_pipe = 0;
    return 1;
  }

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

  closepager(pagerfp);

  /* closing the socket in the background apparently does not work if the whole app exits. So we force the close() call to wait until the data are read by the server */
  mylinger.l_onoff = 1; /* let close wait */
  mylinger.l_linger = 5; /* 5 seconds timeout */
  setsockopt(slvals.n_sockfd, SOL_SOCKET, SO_LINGER, (const void*)&mylinger, sizeof(mylinger));
  n_broken_pipe = 0;
  free(slvals.outbuffer);
  close(slvals.n_sockfd);
  return n_retval;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  make_rawbib() requests a raw bibliography

  int make_rawbib returns 0 if ok, 1 if error

  FILE *fp_infile pointer to a stream to read the id data from

  int n_type type of requested output, see REFXX definitions
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int make_rawbib(FILE* fp_infile, int n_type) {
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char format_string[MAX_FMT_LEN] = "";
  char db[_POSIX_PATH_MAX] = "";
  char pdf_root[_POSIX_PATH_MAX] = "";
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  int n_result;
  int n_done;
  int n_cmdlinerror = 0;
  int numbyte;
  int cs_status;
  size_t byte_written = 0;
  size_t outbuf_len;
  FILE* pagerfp; /* ptr to file */
  struct linger mylinger;
  struct simplelistvals slvals;

   /* get us some buffer for output */
  outbuf_len = 256; /* something to start with */
  slvals.outbuffer = malloc(outbuf_len); 
  if (slvals.outbuffer == NULL) {
    free(slvals.outbuffer);
    return 1;
  }

  strcpy(slvals.outbuffer, "getrefx ");

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

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

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

  if (!*db) {
    fprintf(stderr, "Don't know which database to use. Select one with selectdb or use the -d switch with getref.Stop.\n");
    free(slvals.outbuffer);
    return 1;
  }

  if (n_cmdlinerror) {
    free(slvals.outbuffer);
    return 1;
  }

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

  strcpy(scrambled_passwd, passwd);

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

  /* assemble command string for refdbd */
  switch (n_type) {
  case REFTEIX:
    strcat(slvals.outbuffer, " -t teix -u ");
    break;
  case REFDOCBKX:
    strcat(slvals.outbuffer, " -t db31x -u ");
    break;
  case REFDOCBKX5:
    strcat(slvals.outbuffer, " -t db50x -u ");
    break;
  case REFTEIX5:
    strcat(slvals.outbuffer, " -t tei5x -u ");
    break;
  case REFDOCBK:
    /* fall through */
  default:
    strcat(slvals.outbuffer, " -t db31 -u ");
    break;
  }

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

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

  if (*namespace) {
    strcat(slvals.outbuffer, " -n \"");
    strcat(slvals.outbuffer, namespace);
    strcat(slvals.outbuffer, "\"");
  }

  if (*pdf_root) {
    strcat(slvals.outbuffer, " -R ");
    strcat(slvals.outbuffer, pdf_root);
  }
  
  if (*encoding) {
    strcat(slvals.outbuffer, " -E ");
    strcat(slvals.outbuffer, encoding);
  }
  
  LOG_PRINT(LOG_DEBUG, slvals.outbuffer);

  send_status(slvals.n_sockfd, 0, TERM_NO); /* send step 1 */

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

  numbyte = 0;

  if ((cs_status = read_status(slvals.n_sockfd)) != 0) { /* read step 2 */
    fprintf(stderr, "%s\n", get_status_msg(cs_status));
    close(slvals.n_sockfd);
    free(slvals.outbuffer);
    return 1;
  }

  /* run phases 1 through 4 of client/server protocol */
  /* corresponds to steps 3 to 6 of the getrefx protocol */
  n_result = send_xml_data(fp_infile, /*pagerfp*/stderr, stderr, slvals.n_sockfd, &byte_written);

  if (n_result) {
    free(slvals.outbuffer);
    close(slvals.n_sockfd);
    return 1;
  }

  /* ------------------------------------------------------------ */
  /* PHASE 5 */
  /*  signal server that we're done */

  send_status(slvals.n_sockfd, 402, TERM_NO);

  /* check server status, should be "chunk added successfully" */
  if ((cs_status = read_status(slvals.n_sockfd)) != 403) {
    if (cs_status == 400) {
      size_t byte_written_to_inbuffer;
      int num_trailz;
      int n_read_done = 0;
      /* retrieve server-generated error message */
      do {
	numbyte = tread(slvals.n_sockfd, inbuffer, OUTBUF_LEN);
	/*   	printf("phase4 server reply:%s<<\n", inbuffer); */
	if (numbyte == -1) {
	  /* timeout while reading */
	  fprintf(stderr, "%s", get_status_msg(109));
	  free(slvals.outbuffer);
	  return 1;
	}
	
	/* we rely on the fact that the server reply is no more than
	   OUTBUF_LEN in length */
	if ((num_trailz = get_trailz(inbuffer, numbyte)) >= TERM_LEN) { /* if transmission ends */
	  n_read_done++;
	}
	/* write numbyte chars to output, unless this is the last chunk:
	   we do not want to write the terminating \0 */
	if (!n_broken_pipe) {
	  byte_written_to_inbuffer = fwrite(inbuffer, sizeof(char), numbyte-num_trailz, /*pagerfp*/stderr);
	}
	/* 	printf("%s", inbuffer); */
      } while (!n_read_done);
    }
    else {
      fprintf(stderr, "%s\n", get_status_msg(cs_status));
      free(slvals.outbuffer);
      return 1;
    }
  } /* end if cs_status */

  /* request data */
  send_status(slvals.n_sockfd, 0, TERM_NO); /* send step 7 */

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

  n_done = 0;

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

    cs_status = read_status(slvals.n_sockfd); /* read step 8 */

    if (cs_status != 402 /* last dataset */
	&& cs_status != 404) { /* finished dataset */
      fprintf(stderr, "%s\n", get_status_msg(cs_status));
      if (slvals.n_file_open || slvals.n_file_append) {
	close_outfile(pagerfp);
      }
      else {

	closepager(pagerfp);
      }
      n_broken_pipe = 0;
      free(slvals.outbuffer);
      return 1;
    }
  
    if (cs_status == 402) {
      n_done++;
    }

    byte_written += read_terminated_string(&slvals, pagerfp, &n_error); /* send step 9 */

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

  } while (!n_done);

  /* read summary */
  /*   send_status(slvals.n_sockfd, 0, TERM_NO); */
  cs_status = read_status(slvals.n_sockfd); /* read step 10 */
	
  if (cs_status != 0 /* success */
      && cs_status != 803) { /* partial success */
    fprintf(stderr, "%s\n", get_status_msg(cs_status));
    free(slvals.outbuffer);
    return 1;
  }

  numbyte = tread(slvals.n_sockfd, slvals.inbuffer, OUTBUF_LEN);
  if (numbyte == -1) {
    fprintf(stderr, "%s\n", get_status_msg(109));
    n_broken_pipe = 0;
    free(slvals.outbuffer);
    return 1;
  }
	
  send_status(slvals.n_sockfd, 0, TERM_NO);
      
  if (slvals.n_file_open || slvals.n_file_append) {
    close_outfile(pagerfp);
    fprintf(stderr, "%d byte written to %s\n", byte_written, slvals.outfile);
  }
  else {
    closepager(pagerfp);
  }

  /* closing the socket in the background apparently does not work if the whole app exits. So we force the close() call to wait until the data are read by the server */
  mylinger.l_onoff = 1; /* let close wait */
  mylinger.l_linger = 5; /* 5 seconds timeout */
  setsockopt(slvals.n_sockfd, SOL_SOCKET, SO_LINGER, (const void*)&mylinger, sizeof(mylinger));
  n_broken_pipe = 0;
  free(slvals.outbuffer);
  close(slvals.n_sockfd);

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

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  make_texbib() requests a LaTeX/BibTeX bibliography

  int make_texbib returns 0 if ok, 1 if error

  FILE *fp_infile pointer to a stream to read the id data from

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int make_texbib(FILE* fp_infile) {
  char cmd_buffer[OUTBUF_LEN] = "";
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  int numbyte = 0;
  int n_read_done = 0;
  int result;
  int cs_status;
  int n_done;
  int n_curr_trailing_z = 0;
  int n_last_trailing_z = 0;
  int retval = 0;
  size_t outbuf_len;
  size_t byte_written = 0;
  FILE *pagerfp;
  struct linger mylinger;
  struct simplelistvals slvals;
  struct lilimem sentinel;

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

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

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

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

  strcpy(cmd_buffer, "gettexbib ");

  if (connect_to_server(&(slvals.n_sockfd), server_ip, port_address) != 0) {
    LOG_PRINT(LOG_ERR, "could not connect to server");
    delete_all_lilimem(&sentinel);
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(slvals.n_sockfd, scrambled_passwd, inbuffer)) {
    LOG_PRINT(LOG_ERR, "server authentication failed");
    retval = 1;
    goto Finish;
  }

  /* prepare data */
  result = add_id_from_aux(fp_infile, &(slvals.outbuffer), &outbuf_len);

  if (result) {
    LOG_PRINT(LOG_WARNING, "could not read ID data from .aux file");
    send_status(slvals.n_sockfd, 112, TERM_NO);
    retval = 1;
    goto Finish;
  }

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

  if (*output_format) {
    strcat(cmd_buffer, " -s ");
    strcat(cmd_buffer, output_format);
  }

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

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

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

  /* send command to application server */
  send_status(slvals.n_sockfd, 0, TERM_NO); /* #1 */
  numbyte = tiwrite(slvals.n_sockfd, cmd_buffer, TERM_YES);
  LOG_PRINT(LOG_DEBUG, cmd_buffer);

  if (numbyte == -1) {
    fprintf(stderr, "%s\n", get_status_msg(110));
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    retval = 1;
    goto Finish;
  }


  cs_status = read_status(slvals.n_sockfd); /* #2 */

  if (cs_status) {
    fprintf(stderr, "%s\n", get_status_msg(cs_status));
    LOG_PRINT(LOG_WARNING, get_status_msg(cs_status));
    retval = 1;
    goto Finish;
  }

  /* send id list to server */
  send_status(slvals.n_sockfd, 0, TERM_NO); /* #3 */
  numbyte = tiwrite(slvals.n_sockfd, slvals.outbuffer, TERM_YES);
  LOG_PRINT(LOG_DEBUG, slvals.outbuffer);

  if (numbyte == -1) {
    fprintf(stderr, "%s\n", get_status_msg(110));
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    retval = 1;
    goto Finish;
  }


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

  n_done = 0;

  do { /* loop until we have all requested datasets */
    /* read acknowledgement from application server */
    cs_status = read_status(slvals.n_sockfd); /* #4/#6 */

    if (cs_status != 402
	&& cs_status != 404) {
      fprintf(stderr, "%s\n", get_status_msg(cs_status));
      LOG_PRINT(LOG_WARNING, get_status_msg(cs_status));
      if (slvals.n_file_open || slvals.n_file_append) {
	close_outfile(pagerfp);
      }
      else {
	closepager(pagerfp);
      }
      retval = 1;
      goto Finish;
    }

    if (cs_status == 402) {
      n_done++;
    }

    n_read_done = 0;

    do { /* loop until a terminated string is complete */
      numbyte = tread(slvals.n_sockfd, slvals.inbuffer, OUTBUF_LEN); /* #6 ok */
      if (numbyte == -1) {
	fprintf(stderr, "%s\n", get_status_msg(109));
	if (slvals.n_file_open || slvals.n_file_append) {
	  close_outfile(pagerfp);
	}
	else {
	  closepager(pagerfp);
	}
	n_broken_pipe = 0;
	retval = 1;
	goto Finish;
      }
      
      n_curr_trailing_z = get_trailz(slvals.inbuffer, numbyte);
      
      if (numbyte >= TERM_LEN) {
	if (n_curr_trailing_z >= TERM_LEN) {
	  /* terminator is complete */
	  n_read_done++;
	  /* send back confirmation to the server */
	  send_status(slvals.n_sockfd, 0, TERM_NO); /* #7 */
	}
      }
      else if (n_curr_trailing_z == numbyte
	       && n_curr_trailing_z + n_last_trailing_z >= TERM_LEN) {
	/* terminator is complete including the previous cycle */
	n_read_done++;
	/* send back confirmation to the server */
	send_status(slvals.n_sockfd, 0, TERM_NO); /* #7 */
      }
      else if (n_curr_trailing_z == numbyte) {
	/* terminator is still incomplete */
	n_last_trailing_z += n_curr_trailing_z;
	continue;
      }

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

  /* read summary */
/*   send_status(slvals.n_sockfd, 0, TERM_NO); */
  cs_status = read_status(slvals.n_sockfd); /* #8 */
	
  if (cs_status != 0 /* success */
      && cs_status != 803) { /* partial success */
    fprintf(stderr, "%s\n", get_status_msg(cs_status));
    if (slvals.n_file_open || slvals.n_file_append) {
      close_outfile(pagerfp);
    }
    else {
      closepager(pagerfp);
    }
    retval = 1;
    goto Finish;
  }
	
  numbyte = tread(slvals.n_sockfd, slvals.inbuffer, OUTBUF_LEN); /* #8 ok */
  if (numbyte == -1) {
    fprintf(stderr, "%s\n", get_status_msg(109));
    n_broken_pipe = 0;
    if (slvals.n_file_open || slvals.n_file_append) {
      close_outfile(pagerfp);
    }
    else {
      closepager(pagerfp);
    }
    retval = 1;
    goto Finish;
  }

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

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

 Finish:
  /* closing the socket in the background apparently does not work if
     the whole app exits. So we force the close() call to wait until
     the data are read by the server */
  mylinger.l_onoff = 1; /* let close wait */
  mylinger.l_linger = 5; /* 5 seconds timeout */
  setsockopt(slvals.n_sockfd, SOL_SOCKET, SO_LINGER, (const void*)&mylinger, sizeof(mylinger));

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

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

  void log_print

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

  char* string a string containing the message to be logged

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

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













