/*++++++++++++++++++++++
  refdbdbib.c: refdb application server, bibliography functions
  markus@mhoenicka.de 2000-02-12

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

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

#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdlib.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <expat.h>
#include <iconv.h>
#include <errno.h>
#include <dbi/dbi.h>

#include "backend.h"
#include "linklist.h"
#include "refdb.h"
#include "refdbd.h" /* depends on backend.h */
#include "tokenize.h"
#include "strfncs.h"
#include "connect.h"
#include "xmlhelper.h"
#include "backend-dbib.h"
#include "backend-rtfbib.h"
#include "xmlhandler.h"
#include "xmlout.h"
#include "dbfncs.h"

/* globals */
extern char server_ip[];
extern char main_db[];
extern int n_log_level;
extern const char cs_term[];

/* forward declarations of local functions */
static int send_stylespec(int fd, dbi_conn conn, char* format_string, int n_ref_format);
static int append_return_msg(struct ADDRESULT *ptr_addresult, int n_status, const char* entry_id, dbi_conn conn);
static int sort_entries_asis(dbi_conn conn, struct CLIENT_REQUEST* ptr_clrequest, struct ADDRESULT* ptr_addresult, const char* table_name);
static int fill_in_authoryear_info(struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_cit, struct CLIENT_REQUEST* ptr_clrequest, struct ADDRESULT* ptr_addresult, struct bibinfo* biblio_info, const char* table_name, char* pubtype, struct mset* ptr_mset_sentinel, unsigned long long* nref_counter, struct xmlindent* ptr_indent, int n_ref_format);
static void close_dbi_connections(struct BIBCONNS* ptr_bibconns, const char* drivername);
static int write_bibstyle_info(int fd, const char* bibliofirstindent, const char* biblioblockindent, const char* fontsize, int type);


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  getbib(): executes client command getbib

  int getbib returns 0 on success and 1 on error

  struct CLIENT_REQUEST* ptr_clrequest ptr to structure with client info

  struct bibinfo* biblio_info ptr to a structure containing formatting
                  information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  struct ADDRESULT* ptr_addresult ptr to a structure that will receive
                   the result of the command (number of successful
                   and failed references)

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int getbib(struct CLIENT_REQUEST* ptr_clrequest, struct bibinfo* ptr_biblio_info, int n_ref_format, struct ADDRESULT* ptr_addresult) {
  XML_Parser p;
  struct getbib_data gbdata;
  struct simple_elstack *ptr_sfirst; /* start of the simple element stack */
  dbi_result dbires;
  dbi_result dbires1 = NULL;
  dbi_result dbires_intext;
  dbi_result dbires_cit;
  int nmem_error;
  int ndb_error;
  int n_writeresult;
  int citation_count; /* counter for citations in id handler */
  int xref_count; /* counter for xrefs in id handler */
  int nis_numeric = 0; /* 1 for numeric in-text citation, 0 for author/year */
  int n_currpos;
  int n_is_subseq = 0;
  int n_client_status;
  int retval = 0;
  int n_status;
  int n_send_result;
  size_t inbuffer_len;
  size_t sql_command_len;
  size_t outbuffer_len;
  size_t curr_multi_id_len;
  size_t result_len;
  short int n_pubyear;
  short int title_as_author;
  unsigned long long nref_counter;
  unsigned long long n_id;
  unsigned long long ndb_notfound = 0; /* number of refs not found */
  char xreflabel[128];
  char sql_command1[512];
  char intext_string[8] = "INTEXTS";
  char table_name[] = "getbibtemp";
  char *curr_multi_id = NULL;
  char *sql_command = NULL;
  char *inbuffer = NULL;
  char *outbuffer = NULL;
  char *id_string = NULL;
  char *myjournal = NULL;
  const char *sql_errmsg = NULL;
  const char *drivername;
  char *prev_authorconcat = NULL;
  const char *bibsequence = NULL;
  const char *intextsequence = NULL;
  const char* item = NULL;
  const char* db_encoding = NULL;
  struct BIBCONNS bibconns;
  struct lilimem sentinel;
  Lilifstring notfound_first;
  Lilifstring notformatted_first;
  iconv_t conv_descriptor = NULL;
  struct xmlindent xindent;

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

  /*initialize xindent. DocBook starts with a bibliography, TEI
    starts with a div/listBibl */
  initialize_xmlindent(&xindent, (n_ref_format == REFTEIX5) ? 2*INDENT_INC:INDENT_INC, indent_notbelow_dbib, is_entry_dbib);

  notfound_first.ptr_next = NULL;
  *(notfound_first.token) = '\0';

  notformatted_first.ptr_next = NULL;
  *(notformatted_first.token) = '\0';

  ptr_biblio_info->entry_id = NULL;
  ptr_biblio_info->xreflabel = xreflabel;

  bibconns.conn = NULL; /* connection to the default reference database */
  bibconns.conn_refdb = NULL; /* connection to refdb */
  bibconns.conn_source = NULL; /* connection to source reference database */

  nref_counter = ptr_biblio_info->n_startnumber - 1; /* numbering starts here */

  sql_command_len = 8192;
  sql_command = malloc(sql_command_len); 
  if (sql_command == NULL || insert_lilimem(&sentinel, (void**)&sql_command, NULL)) {
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 1;
  }

  outbuffer_len = 8192;
  outbuffer = malloc(outbuffer_len);
  if (outbuffer == NULL || insert_lilimem(&sentinel, (void**)&outbuffer, NULL)) {
    delete_all_lilimem(&sentinel);
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 1;
  }
  *outbuffer = '\0';

  curr_multi_id_len = 1024;
  curr_multi_id = malloc(curr_multi_id_len);
  if (curr_multi_id == NULL || insert_lilimem(&sentinel, (void**)&curr_multi_id, NULL)) { 
    delete_all_lilimem(&sentinel);
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 1;
  }
  *curr_multi_id = '\0';

  inbuffer_len = (size_t)COMMAND_INBUF_LEN;
  inbuffer = malloc(inbuffer_len);
  if (inbuffer == NULL || insert_lilimem(&sentinel, (void**)&inbuffer, NULL)) {  /* malloc failed */
    delete_all_lilimem(&sentinel);
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 1;
  }

  /* initialize "globals" */
  nmem_error = 0;
  ndb_error = 0;
  ptr_sfirst = NULL;
  citation_count = 0;
  xref_count = 0;

  /* create the parser instance */
  p = XML_ParserCreate(NULL);
  if (!p) {
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    delete_all_lilimem(&sentinel);
    return 1;
  }

  /* register our handlers. these handlers will be called whenever
     expat finds a start- or endtag or character data */
  XML_SetElementHandler(p, id_start_handler, id_end_handler);

  XML_SetCharacterDataHandler(p, id_char_handler);

  /* connect to the default reference database as provided by the client */
  if ((bibconns.conn = connect_to_db(ptr_clrequest, NULL, 0)) == NULL) {
    /* if database server connect failed */
    send_status(ptr_clrequest->fd, 204, TERM_NO);
    LOG_PRINT(LOG_WARNING, get_status_msg(204));
    retval = 1;
    goto Finish;
  }

  drivername = dbi_driver_get_name(dbi_conn_get_driver(bibconns.conn));

  /* connect to the refdb database */
  if (!strcmp(my_dbi_conn_get_cap(bibconns.conn, "multiple_db"), "t")) {
    /* reuse the existing connection */
    bibconns.conn_refdb = bibconns.conn;
  }
  else {
    bibconns.conn_refdb = connect_to_db(ptr_clrequest, main_db, 0);
    if (!bibconns.conn_refdb) {
      send_status(ptr_clrequest->fd, 202, TERM_NO);
      LOG_PRINT(LOG_WARNING, get_status_msg(202));
      retval = 1;
      goto Finish;
    }
  }

  /* get the database encoding */
  db_encoding = dbi_conn_get_encoding(bibconns.conn);

  /* if we need to convert, create a conversion descriptor for iconv() */
  if (db_encoding && *(ptr_biblio_info->encoding) && strcmp(db_encoding, ptr_biblio_info->encoding)) {
    char to_encoding[64];

    if (!strcmp(ptr_biblio_info->encoding, "US-ASCII")) {
      strcpy(to_encoding, "ASCII//TRANSLIT");
    }
    else {
      snprintf(to_encoding, 64, "%s//TRANSLIT", ptr_biblio_info->encoding);
    }

    conv_descriptor = iconv_open(to_encoding, !strcmp(db_encoding, "US-ASCII") ? "ASCII" : db_encoding);
    if (conv_descriptor == (iconv_t)(-1)) {
      LOG_PRINT(LOG_WARNING, get_status_msg(701));
      send_status(ptr_clrequest->fd, 701, TERM_NO);
      retval = 1;
      goto Finish;
    }
    else {
      LOG_PRINT(LOG_DEBUG, "database encoding is:");
      LOG_PRINT(LOG_DEBUG, db_encoding);
    }
  }
  else {
    conv_descriptor = NULL;
    LOG_PRINT(LOG_DEBUG, "no character encoding conversion required");
  }
    
  LOG_PRINT(LOG_DEBUG, "output encoding is:");
  LOG_PRINT(LOG_DEBUG, ptr_biblio_info->encoding);

  myjournal = strdup(ptr_biblio_info->format_string);
  if (!myjournal || insert_lilimem(&sentinel, (void**)&myjournal, NULL)) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    retval = 1;
    goto Finish;
  }

  if (dbi_conn_quote_string(bibconns.conn_refdb, &myjournal) == 0) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    retval = 1;
    goto Finish;
  }

  /* send the style specification first */
  n_send_result = send_stylespec(ptr_clrequest->fd, bibconns.conn_refdb, myjournal, n_ref_format);

  if (n_send_result != 0 && n_send_result != 241) {
    /* write error */
    retval = 1;
    goto Finish;
  }

  n_client_status = read_status(ptr_clrequest->fd);
    
  if (n_client_status != 0) {
    LOG_PRINT(LOG_INFO, get_status_msg(112));
    retval = 1;
    goto Finish;
  }


    /* create a temporary table for sorting the result */
    /* both MySQL and PostgreSQL use separate namespaces per
       connection so it is safe to reuse the same table name */

    /* the columns mean : */
    /*
      dbname  the name of the database the ref is taken from
      orig_id the numeric ID of the entry in the reference database
      entry_id the ID string as taken from the foo.id.xml file
      article_title the title of the work
      author_concat the concatenated author string
      periodical the journal name
      volume 
      issue 
      startpage 
      pubyear 
      citation_pos position of the citation in the document, 0-based, identical for all references of a multiple citation
      xref_pos position of the reference within a citation, 0-based and thus 0 for all references which are not part of a multiple citation
      multi_id ID string of the multiple citation a ref is part of, if any
      sorted_pos position of the ref in the bibliography after sorting
      author_abbrevlist the proper et al.-like string
      year_uni_suffix the unique suffix for the pubyear, if any
      
    */


  if (!strcmp(ptr_clrequest->dbserver, "mysql")) {
    sprintf(sql_command, 
	    "CREATE TEMPORARY TABLE %s (id BIGINT NOT NULL AUTO_INCREMENT,"
	    "dbname VARCHAR(64),"
	    "orig_id BIGINT,"
	    "entry_id TEXT,"
	    "article_title TEXT,"
	    "author_concat TEXT,"
	    "periodical VARCHAR(255),"
	    "volume VARCHAR(255),"
	    "issue VARCHAR(255),"
	    "startpage VARCHAR(255),"
	    "pubyear SMALLINT,"
	    "monthday VARCHAR(16),"
	    "citation_pos INT,"
	    "xref_pos INT,"
	    "multi_id TEXT,"
	    "sorted_pos INT DEFAULT 1,"
	    "author_abbrevlist TEXT,"
	    "year_uni_suffix VARCHAR(16),"
	    "title_as_author SMALLINT DEFAULT 0,"
	    "citekey VARCHAR(255),"
	    "PRIMARY KEY (id))", table_name);
  }
  else if (!strcmp(ptr_clrequest->dbserver, "pgsql")) {
    sprintf(sql_command, 
	    "CREATE TEMPORARY TABLE %s (id BIGSERIAL,"
	    "dbname VARCHAR(64),"
	    "orig_id BIGINT,"
	    "entry_id TEXT,"
	    "article_title TEXT,"
	    "author_concat TEXT,"
	    "periodical VARCHAR(255),"
	    "volume VARCHAR(255),"
	    "issue VARCHAR(255),"
	    "startpage VARCHAR(255),"
	    "pubyear SMALLINT,"
	    "monthday VARCHAR(16),"
	    "citation_pos INTEGER,"
	    "xref_pos INTEGER,"
	    "multi_id TEXT,"
	    "sorted_pos INTEGER DEFAULT 1,"
	    "author_abbrevlist TEXT,"
	    "year_uni_suffix VARCHAR(16),"
	    "title_as_author SMALLINT DEFAULT 0,"
	    "citekey VARCHAR(255),"
	    "PRIMARY KEY (id))", table_name);
  }
  else if (!strcmp(ptr_clrequest->dbserver, "sqlite")) {
    sprintf(sql_command,
	    "CREATE TEMPORARY TABLE %s (id INTEGER PRIMARY KEY,"
	    "dbname VARCHAR(64),"
	    "orig_id INTEGER,"
	    "entry_id TEXT,"
	    "article_title TEXT,"
	    "author_concat TEXT,"
	    "periodical TEXT,"
	    "volume TEXT,"
	    "issue TEXT,"
	    "startpage TEXT,"
	    "pubyear SMALLINT,"
	    "monthday TEXT,"
	    "citation_pos INTEGER,"
	    "xref_pos INTEGER,"
	    "multi_id TEXT,"
	    "sorted_pos INTEGER DEFAULT 1,"
	    "author_abbrevlist TEXT,"
	    "title_as_author SMALLINT DEFAULT 0,"
	    "citekey TEXT,"
	    "year_uni_suffix TEXT)", table_name);
  }
  else if (!strcmp(ptr_clrequest->dbserver, "sqlite3")) {
    sprintf(sql_command, 
	    "CREATE TEMPORARY TABLE %s (id INTEGER PRIMARY KEY,"
	    "dbname VARCHAR(64),"
	    "orig_id BIGINT,"
	    "entry_id TEXT,"
	    "article_title TEXT,"
	    "author_concat TEXT,"
	    "periodical TEXT,"
	    "volume TEXT,"
	    "issue TEXT,"
	    "startpage TEXT,"
	    "pubyear SMALLINT,"
	    "monthday TEXT,"
	    "citation_pos INTEGER,"
	    "xref_pos INTEGER,"
	    "multi_id TEXT,"
	    "sorted_pos INTEGER DEFAULT 1,"
	    "author_abbrevlist TEXT,"
	    "title_as_author SMALLINT DEFAULT 0,"
	    "citekey TEXT,"
	    "year_uni_suffix TEXT)", table_name);
  }

  LOG_PRINT(LOG_DEBUG, sql_command);

  /* the temporary table will be created in the default database */
  dbires = dbi_conn_query(bibconns.conn, sql_command);
  if (!dbires) {
    dbi_conn_error(bibconns.conn, &sql_errmsg);
    if (sql_errmsg) {
      send_status(ptr_clrequest->fd, 242, TERM_NO);
      LOG_PRINT(LOG_WARNING, (char*)sql_errmsg);
    }
    else {
      send_status(ptr_clrequest->fd, 242, TERM_NO);
      LOG_PRINT(LOG_WARNING, get_status_msg(242));
    }
    retval = 1;
    goto Finish;
  }

  dbi_result_free(dbires);
  dbires = NULL;

  /* make data available for handlers */
  gbdata.ptr_default_db = ptr_clrequest->current_db;
  gbdata.conn = bibconns.conn; /* connection to the temp table db */
  gbdata.conn_refdb = bibconns.conn_refdb; /* connection to the style db */
  gbdata.ptr_citation_count = &citation_count;
  gbdata.ptr_xref_count = &xref_count;
  gbdata.ptr_table_name = table_name;
  gbdata.ptr_curr_multi_id = curr_multi_id;
  gbdata.quoted_journal = myjournal;
  gbdata.ptr_cmid_len = &curr_multi_id_len;
  gbdata.ptr_sfirst = ptr_sfirst;
  gbdata.ptr_nmem_error = &nmem_error;
  gbdata.ptr_ndb_error = &ndb_error;
  gbdata.ptr_ndb_notfound = &(ptr_addresult->skipped);
  gbdata.ptr_clrequest = ptr_clrequest;
  gbdata.ptr_notfound_first = &notfound_first;
  gbdata.ptr_found_first = NULL; /* not used here */

  XML_SetUserData(p, (void*)&gbdata);


  /* tell client that we're ready for XML data */
  send_status(ptr_clrequest->fd, 0, TERM_NO);
  
  /* read XML data from client */
  if (!read_xml(ptr_clrequest->fd, p, ptr_addresult)) { /* error */
    dbi_conn_error(bibconns.conn, &sql_errmsg);
    LOG_PRINT(LOG_WARNING, (char*)sql_errmsg);
    sprintf(sql_command, "DROP TABLE %s", table_name);
    LOG_PRINT(LOG_DEBUG, sql_command);
    dbires = dbi_conn_query(bibconns.conn, sql_command);
    if (!dbires) {
      LOG_PRINT(LOG_WARNING, get_status_msg(243));
    }
    dbi_result_free(dbires);
    dbires = NULL;
    delete_idlist(ptr_sfirst);
    retval = 1;
    goto Finish;
  }
  else if (nmem_error) {
    retval = 1;
    goto Finish;
  }      
  else if (ndb_error) {
    dbi_conn_error(bibconns.conn, &sql_errmsg);
    LOG_PRINT(LOG_WARNING, (char*)sql_errmsg);
    retval = 1;
    goto Finish;
  }      


  n_client_status = read_status(ptr_clrequest->fd);
    
  if (n_client_status != 0) {
    LOG_PRINT(LOG_INFO, get_status_msg(112));
    retval = 1;
    goto Finish;
  }

  /* see how the references should be sorted */
  if (!strcmp(my_dbi_conn_get_cap(bibconns.conn_refdb, "multiple_db"), "t")) {
    sprintf(sql_command, "SELECT BIBLIOSEQUENCE,INTEXTSEQUENCE from %s.CITSTYLE where JOURNAL=%s", main_db, myjournal);
  }
  else {
    sprintf(sql_command, "SELECT BIBLIOSEQUENCE,INTEXTSEQUENCE from CITSTYLE where JOURNAL=%s", myjournal);
  }

  LOG_PRINT(LOG_DEBUG, sql_command);
  dbires = dbi_conn_query(bibconns.conn_refdb, sql_command);



  /* todo: figure out what to do with append_return_msg and such
   we have to send:
  - style data
  - the bibliographic data proper
  - an error list
  - a command summary */


  if (!dbires || dbi_result_next_row(dbires) == 0) {
    if (dbires) {
      dbi_result_free(dbires);
      dbires = NULL;
    }
    send_status(ptr_clrequest->fd, 241, TERM_NO);
    dbi_conn_error(bibconns.conn_refdb, &sql_errmsg);
    LOG_PRINT(LOG_WARNING, sql_errmsg);
    retval = 1;
    goto Finish;
  }

  /* this is the sequence of the entries in the bibliography */
  bibsequence = dbi_result_get_string(dbires, "BIBLIOSEQUENCE");
    
  if (!bibsequence || !strcmp(bibsequence, "ERROR")) {
    send_status(ptr_clrequest->fd, 241, TERM_NO);
    dbi_conn_error(bibconns.conn_refdb, &sql_errmsg);
    LOG_PRINT(LOG_WARNING, sql_errmsg);
    dbi_result_free(dbires);
    dbires = NULL;
    retval = 1;
    goto Finish;
  }

  /* this is the sequence of references in a multiple citation */
  intextsequence = dbi_result_get_string(dbires, "INTEXTSEQUENCE");
    
  if (!intextsequence || !strcmp(intextsequence, "ERROR")) {
    send_status(ptr_clrequest->fd, 241, TERM_NO);
    dbi_conn_error(bibconns.conn_refdb, &sql_errmsg);
    LOG_PRINT(LOG_WARNING, sql_errmsg);
    dbi_result_free(dbires);
    dbires = NULL;
    retval = 1;
    goto Finish;
  }

  if (strcmp(bibsequence, "BASIS") == 0) {
    /* sort the entries according to their appearance in the document */
    if ((n_status = sort_entries_asis(bibconns.conn, ptr_clrequest, ptr_addresult, table_name)) != 0) {
      send_status(ptr_clrequest->fd, n_status, TERM_NO);
      append_return_msg(ptr_addresult, n_status, NULL, bibconns.conn);
      dbi_result_free(dbires);
      dbires = NULL;
      retval = 1;
      goto Finish;
    }

    /* now do the real query. We have to take care of the fact that
       the sorting order, although "ASIS", may be affected by the
       sorting order of multiple in-text citations */
    if (strcmp(intextsequence, "ASIS") == 0) {
      sprintf(sql_command, "SELECT dbname, orig_id, author_concat, pubyear, article_title, periodical, volume, issue, startpage, entry_id, year_uni_suffix, sorted_pos, title_as_author FROM %s WHERE sorted_pos>0 ORDER BY id", table_name);
    }
    else if (strcmp(intextsequence, "AUTHORDATE") == 0) {
      sprintf(sql_command, "SELECT dbname, orig_id, author_concat, pubyear, article_title, periodical, volume, issue, startpage, entry_id, year_uni_suffix, sorted_pos, title_as_author FROM %s WHERE sorted_pos>0 ORDER BY citation_pos, author_concat, pubyear, monthday, periodical, volume, issue, startpage", table_name);
    }
    else if (strcmp(intextsequence, "CITEKEY") == 0) {
      sprintf(sql_command, "SELECT dbname, orig_id, author_concat, pubyear, article_title, periodical, volume, issue, startpage, entry_id, year_uni_suffix, sorted_pos, title_as_author FROM %s WHERE sorted_pos>0 ORDER BY citation_pos, citekey, author_concat, pubyear, monthday, periodical, volume, issue, startpage", table_name);
    }
    else if (strcmp(intextsequence, "DATEASC") == 0) {
      sprintf(sql_command, "SELECT dbname, orig_id, author_concat, pubyear, article_title, periodical, volume, issue, startpage, entry_id, year_uni_suffix, sorted_pos, title_as_author FROM %s WHERE sorted_pos>0 ORDER BY citation_pos, pubyear, monthday, author_concat, periodical, volume, issue, startpage", table_name);
    }
    else if (strcmp(intextsequence, "DATEDESC") == 0) {
      sprintf(sql_command, "SELECT dbname, orig_id, author_concat, pubyear, article_title, periodical, volume, issue, startpage, entry_id, year_uni_suffix, sorted_pos, title_as_author FROM %s WHERE sorted_pos>0 ORDER BY citation_pos, pubyear DESC, monthday DESC, author_concat, periodical, volume, issue, startpage", table_name);
    }
  }
  else if (strcmp(bibsequence, "BAUTHORDATE") == 0) {
    sprintf(sql_command, "SELECT DISTINCT dbname, orig_id, upper(author_concat), pubyear, article_title, periodical, volume, issue, startpage, entry_id, year_uni_suffix, sorted_pos, monthday, title_as_author FROM %s ORDER BY upper(author_concat), pubyear, monthday, volume, issue, article_title, periodical, startpage", table_name);
  }
  else if (strcmp(bibsequence, "BCITEKEY") == 0) {
    sprintf(sql_command, "SELECT DISTINCT dbname, orig_id, author_concat, pubyear, article_title, periodical, volume, issue, startpage, entry_id, year_uni_suffix, sorted_pos, monthday, title_as_author, citekey FROM %s ORDER BY citekey, author_concat, pubyear, monthday, volume, issue, article_title, periodical, startpage", table_name);
  }
  else if (strcmp(bibsequence, "BDATEASC") == 0) {
    sprintf(sql_command, "SELECT DISTINCT dbname, orig_id, author_concat, pubyear, article_title, periodical, volume, issue, startpage, entry_id, year_uni_suffix, sorted_pos, monthday, title_as_author FROM %s ORDER BY pubyear, monthday, author_concat, volume, issue, article_title, periodical, startpage", table_name);
  }
  else if (strcmp(bibsequence, "BDATEDESC") == 0) {
    sprintf(sql_command, "SELECT DISTINCT dbname, orig_id, author_concat, pubyear, article_title, periodical, volume, issue, startpage, entry_id, year_uni_suffix, sorted_pos, monthday, title_as_author FROM %s ORDER BY pubyear DESC, monthday DESC, author_concat, volume, issue, article_title, periodical, startpage", table_name);
  }

  dbi_result_free(dbires);
  dbires = NULL;

  LOG_PRINT(LOG_DEBUG, sql_command);
  dbires = dbi_conn_query(bibconns.conn, sql_command);
  if (!dbires) {
    send_status(ptr_clrequest->fd, 234, TERM_NO);
    dbi_conn_error(bibconns.conn_refdb, &sql_errmsg);
    LOG_PRINT(LOG_WARNING, sql_errmsg);
    retval = 1;
    goto Finish;
  }

  /* retrieve basic information about the bibliography style from CITSTYLE into ptr_result */
  if (!strcmp(my_dbi_conn_get_cap(bibconns.conn_refdb, "multiple_db"), "t")) {
    sprintf(sql_command1, "SELECT ID, CITSEPARATOR, FOLLOWING, PRECEEDING, RANGESEPARATOR, INTEXTSEQUENCE, STYLE from %s.CITSTYLE where JOURNAL=%s", main_db, myjournal);
  }
  else {
    sprintf(sql_command1, "SELECT ID, CITSEPARATOR, FOLLOWING, PRECEEDING, RANGESEPARATOR, INTEXTSEQUENCE, STYLE from CITSTYLE where JOURNAL=%s", myjournal);
  }

  LOG_PRINT(LOG_DEBUG, sql_command1);
  dbires_cit = dbi_conn_query(bibconns.conn_refdb, sql_command1);

  if (!dbires_cit || dbi_result_next_row(dbires_cit) == 0) {
    if (dbires_cit) {
      dbi_result_free(dbires_cit);
      dbires_cit = NULL;
    }
    send_status(ptr_clrequest->fd, 234, TERM_NO);
    dbi_conn_error(bibconns.conn_refdb, &sql_errmsg);
    LOG_PRINT(LOG_WARNING, sql_errmsg);
    dbi_result_free(dbires);
    dbires = NULL;
    retval = 1;
    goto Finish;
  }
  

  /* decide whether this is a numeric citation */
  /* find the first position in the intext citation; if it is refnumber,
     don't hassle with the following */
  if (!strcmp(my_dbi_conn_get_cap(bibconns.conn_refdb, "multiple_db"), "t")) {
    sprintf(sql_command1, "SELECT %s.POSITIONS.TYPE FROM %s.POSITIONS INNER JOIN %s.REFSTYLE ON %s.POSITIONS.REFSTYLEID=%s.REFSTYLE.ID INNER JOIN %s.CITSTYLE ON %s.REFSTYLE.CITSTYLEID=%s.CITSTYLE.ID WHERE %s.POSITIONS.POS=0 AND %s.REFSTYLE.PUBTYPE='INTEXT' AND %s.CITSTYLE.JOURNAL=%s", main_db, main_db, main_db, main_db, main_db, main_db, main_db, main_db, main_db, main_db, main_db, myjournal);
  }
  else {
    sprintf(sql_command1, "SELECT POSITIONS.TYPE FROM POSITIONS INNER JOIN REFSTYLE ON POSITIONS.REFSTYLEID=REFSTYLE.ID INNER JOIN CITSTYLE ON REFSTYLE.CITSTYLEID=CITSTYLE.ID WHERE POSITIONS.POS=0 AND REFSTYLE.PUBTYPE='INTEXT' AND CITSTYLE.JOURNAL=%s", myjournal);
  }

  LOG_PRINT(LOG_DEBUG, sql_command1);
  dbires_intext = dbi_conn_query(bibconns.conn_refdb, sql_command1);

  if (!dbires_intext) {
    send_status(ptr_clrequest->fd, 234, TERM_NO);
    dbi_conn_error(bibconns.conn_refdb, &sql_errmsg);
    LOG_PRINT(LOG_WARNING, sql_errmsg);
    dbi_result_free(dbires_cit);
    dbires_cit = NULL;
    dbi_result_free(dbires);
    dbires_cit = NULL;
    retval = 1;
    goto Finish;
  }
  
  if (dbi_result_next_row(dbires_intext) != 0) {
    item = dbi_result_get_string_idx(dbires_intext, 1/*POSITIONS.TYPE*/);
    /* citekey-based citation styles are treated as numeric */
    if (strcmp(item, "REFNUMBER") == 0
	|| strcmp(item, "CITEKEY") == 0) {
      nis_numeric = 1;
    }
  }
  else { /* pretend numeric */
    nis_numeric = 1; 
  }
  dbi_result_free(dbires_intext);
  dbires_intext = NULL;
    
  if (!nis_numeric) { /* only useful for author/year style */
    if ((n_status = fill_in_authoryear_info(&bibconns, dbires, dbires_cit, ptr_clrequest, ptr_addresult, ptr_biblio_info, table_name, intext_string, NULL /* don't need bibliomsets */, &nref_counter, &xindent, n_ref_format)) != 0) {
      send_status(ptr_clrequest->fd, n_status, TERM_NO);
      dbi_result_free(dbires_cit);
      dbires_cit = NULL;
      dbi_result_free(dbires);
      dbires = NULL;
      retval = 1;
      goto Finish;
    }

    dbi_result_free(dbires);
    dbires = NULL;

    /* reuse previous query string */
    LOG_PRINT(LOG_DEBUG, sql_command);
    dbires = dbi_conn_query(bibconns.conn, sql_command);
    if (!dbires) {
      dbi_result_free(dbires_cit);
      dbires_cit = NULL;
      send_status(ptr_clrequest->fd, 234, TERM_NO);
      dbi_conn_error(bibconns.conn_refdb, &sql_errmsg);
      LOG_PRINT(LOG_WARNING, sql_errmsg);
      retval = 1;
      goto Finish;
    }
  }
  dbi_result_free(dbires_cit);
  dbires_cit = NULL;



  /* output actually starts here */
  /* create doctype line etc */
  if (n_ref_format == REFRTF) {
    if ((n_status = prepare_render_rtfbib(&outbuffer, &outbuffer_len, ptr_biblio_info, bibconns.conn_refdb)) != 0) {
      send_status(ptr_clrequest->fd, n_status, TERM_NO);
      append_return_msg(ptr_addresult, n_status, NULL, bibconns.conn);
      retval = 1;
      dbi_result_free(dbires);
      dbires = NULL;
      goto Finish;
    }
  }
  else {
    if ((n_status = prepare_render_dbib(&outbuffer, &outbuffer_len, ptr_biblio_info, bibconns.conn_refdb, n_ref_format, ptr_clrequest->namespace)) != 0) {
      send_status(ptr_clrequest->fd, n_status, TERM_NO);
      append_return_msg(ptr_addresult, n_status, NULL, bibconns.conn);
      retval = 1;
      dbi_result_free(dbires);
      dbires = NULL;
      goto Finish;
    }
  }

  /* loop over all distinct references for the actual formatting */
  while (dbi_result_next_row(dbires)) {

    /* the following loop has already run if !nis_numeric */
    if (nis_numeric) {
      nref_counter++; /* will start at 1 as is common in bibliographies */
      id_string = my_dbi_result_get_string_copy(dbires, "entry_id");
      if (!id_string || !strcmp(id_string, "ERROR") || insert_lilimem(&sentinel, (void**)&id_string, "id_string")) {
	ptr_addresult->failure++;
	append_return_msg(ptr_addresult, 234, id_string, bibconns.conn);
	goto FinishLoop;
      }
      sprintf(sql_command1, "UPDATE %s SET sorted_pos="ULLSPEC" WHERE entry_id='%s'", table_name, (unsigned long long)nref_counter, id_string);
      LOG_PRINT(LOG_DEBUG, sql_command1);
      dbires1 = dbi_conn_query(bibconns.conn, sql_command1);
      if (!dbires1) {
	ptr_addresult->failure++;
	append_return_msg(ptr_addresult, 234, id_string, bibconns.conn);
	goto FinishLoop;
      }
      dbi_result_free(dbires1);
      dbires1 = NULL;
      delete_lilimem(&sentinel, "id_string");
    }

    /* 0  refdb_id             ID  - 
       1  refdb_type           TY  - 
       2  refdb_pubyear        PY  - (partial)
       3  refdb_startpage      SP  - 
       4  refdb_endpage        EP  - 
       5  refdb_abstract       N2  - 
       6  refdb_title          TI  - 
       7  refdb_volume         VL  - 
       8  refdb_issue          CP  - 
       9  refdb_booktitle      BT  - 
       10  refdb_city           CY  - 
       11  refdb_publisher      PB  - 
       12  refdb_title_series   T3  - 
       13  refdb_address        AD  - 
       deleted 14  refdb_url            UR  - 
       14  refdb_issn           SN  - 
       15  refdb_periodical_id  JO  - (indirect)
       16  refdb_pyother_info   PY  - (partial)
       17  refdb_secyear        Y2  - (partial)
       18  refdb_secother_info  Y2  - (partial)
       19  refdb_user1          U1  - 
       20  refdb_user2          U2  - 
       21  refdb_user3          U3  - 
       22  refdb_user4          U4  - 
       23  refdb_user5          U5  - 
    */

    item = dbi_result_get_string(dbires, "dbname");
    n_id = my_dbi_result_get_idval(dbires, "orig_id");
/*     printf("about to format %qu<<\n", n_id); */

    if (!strcmp(my_dbi_conn_get_cap(bibconns.conn, "multiple_db"), "t")) {
      sprintf(sql_command, "SELECT %s.t_refdb.refdb_id, %s.t_refdb.refdb_type, %s.t_refdb.refdb_pubyear, %s.t_refdb.refdb_startpage, %s.t_refdb.refdb_endpage, %s.t_refdb.refdb_abstract, %s.t_refdb.refdb_title, %s.t_refdb.refdb_volume, %s.t_refdb.refdb_issue, %s.t_refdb.refdb_booktitle, %s.t_refdb.refdb_city, %s.t_refdb.refdb_publisher, %s.t_refdb.refdb_title_series, %s.t_refdb.refdb_address, %s.t_refdb.refdb_issn, %s.t_refdb.refdb_periodical_id, %s.t_refdb.refdb_pyother_info, %s.t_refdb.refdb_secyear, %s.t_refdb.refdb_secother_info, %s.t_refdb.refdb_user1, %s.t_refdb.refdb_user2, %s.t_refdb.refdb_user3, %s.t_refdb.refdb_user4, %s.t_refdb.refdb_user5, %s.t_refdb.refdb_typeofwork, %s.t_refdb.refdb_area, %s.t_refdb.refdb_ostype, %s.t_refdb.refdb_degree, %s.t_refdb.refdb_runningtime, %s.t_refdb.refdb_classcodeintl, %s.t_refdb.refdb_classcodeus, %s.t_refdb.refdb_senderemail, %s.t_refdb.refdb_recipientemail, %s.t_refdb.refdb_mediatype, %s.t_refdb.refdb_numvolumes, %s.t_refdb.refdb_edition, %s.t_refdb.refdb_computer, %s.t_refdb.refdb_conferencelocation, %s.t_refdb.refdb_registrynum, %s.t_refdb.refdb_classification, %s.t_refdb.refdb_section, %s.t_refdb.refdb_pamphletnum, %s.t_refdb.refdb_chapternum, %s.t_refdb.refdb_citekey FROM %s.t_refdb WHERE %s.t_refdb.refdb_id="ULLSPEC, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, item, (unsigned long long)n_id);
      /* reuse existing connection */
      bibconns.conn_source = bibconns.conn;
    }
    else {
      /* get new connection to source reference database */
      bibconns.conn_source = connect_to_db(ptr_clrequest, item, 0);
      if (!bibconns.conn_source) {
	ptr_addresult->failure++;
	send_status(ptr_clrequest->fd, 204, TERM_NO);
	append_return_msg(ptr_addresult, 204, id_string, bibconns.conn);
	dbi_result_free(dbires);
	dbires = NULL;
	retval = 1;
	goto Finish;
      }
      sprintf(sql_command, "SELECT refdb_id, refdb_type, refdb_pubyear, refdb_startpage, refdb_endpage, refdb_abstract, refdb_title, refdb_volume, refdb_issue, refdb_booktitle, refdb_city, refdb_publisher, refdb_title_series, refdb_address, refdb_issn, refdb_periodical_id, refdb_pyother_info, refdb_secyear, refdb_secother_info, refdb_user1, refdb_user2, refdb_user3, refdb_user4, refdb_user5, refdb_typeofwork, refdb_area, refdb_ostype, refdb_degree, refdb_runningtime, refdb_classcodeintl, refdb_classcodeus, refdb_senderemail, refdb_recipientemail, refdb_mediatype, refdb_numvolumes, refdb_edition, refdb_computer, refdb_conferencelocation, refdb_registrynum, refdb_classification, refdb_section, refdb_pamphletnum, refdb_chapternum, refdb_citekey FROM t_refdb WHERE refdb_id="ULLSPEC, (unsigned long long)n_id);
    }
      
    LOG_PRINT(LOG_DEBUG, sql_command);
    dbires1 = dbi_conn_query(bibconns.conn_source, sql_command);

    if (!dbires1 || dbi_result_next_row(dbires1) == 0) {
      if (dbires1) {
	dbi_result_free(dbires1);
	dbires1 = NULL;
      }
      ptr_addresult->failure++;
      append_return_msg(ptr_addresult, 234, id_string, bibconns.conn);
      goto FinishLoop;
    }
      	  
    ptr_biblio_info->entry_id = my_dbi_result_get_string_copy(dbires, "entry_id");
    if (!ptr_biblio_info->entry_id || !strcmp(ptr_biblio_info->entry_id, "ERROR") || insert_lilimem(&sentinel, (void**)&(ptr_biblio_info->entry_id), "id_string")) {
      ptr_addresult->failure++;
      append_return_msg(ptr_addresult, 234, id_string, bibconns.conn);
      goto FinishLoop;
    }

    ptr_biblio_info->year_unique_suffix = dbi_result_get_string(dbires, "year_uni_suffix");


    /* compare current string with the string of the previous iteration */
    /* we have to work around a MySQL peculiarity as it returns NULL if
       the string is empty */
/*     item = my_dbi_result_get_string_copy(dbires, "author_concat"); */
    item = my_dbi_result_get_string_copy_idx(dbires, 3);
/*     printf("author_concat went to: %s<<\n", item); */
    if ((!item && my_dbi_conn_error_flag(bibconns.conn)) || (item && !strcmp(item, "ERROR"))) {
      ptr_addresult->failure++;
      append_return_msg(ptr_addresult, 234, id_string, bibconns.conn);
      goto FinishLoop;
    }

    n_currpos = dbi_result_get_int(dbires, "sorted_pos");
    if ((!n_currpos && my_dbi_conn_error_flag(bibconns.conn))) {
      ptr_addresult->failure++;
      append_return_msg(ptr_addresult, 234, id_string, bibconns.conn);
      goto FinishLoop;
    }

    n_pubyear = dbi_result_get_short(dbires, "pubyear");
    if ((!n_pubyear && my_dbi_conn_error_flag(bibconns.conn))) {
      ptr_addresult->failure++;
      append_return_msg(ptr_addresult, 234, id_string, bibconns.conn);
      goto FinishLoop;
    }

    /* empty authorlists are never treated as subseq. Both the current
       and the previous authorlist may be NULL */
    n_is_subseq = check_is_subseq(item, table_name, n_currpos, n_pubyear, bibconns.conn);

    if (n_is_subseq == -1) {
      ptr_addresult->failure++;
      append_return_msg(ptr_addresult, 234, id_string, bibconns.conn);
      goto FinishLoop;
    }

    ptr_biblio_info->is_subseq = n_is_subseq;

    /* free memory */
    if (prev_authorconcat) {
      free(prev_authorconcat);
    }

    /* keep the current string so we can compare it in the next iteration */
    prev_authorconcat = (char*)item;

    item = dbi_result_get_string(dbires, "dbname");

    title_as_author = dbi_result_get_short(dbires, "title_as_author");

    /* todo: break instead of exit on error? */
    if (n_ref_format == REFRTF) {
      if ((n_status = render_rtfbib(&outbuffer, &outbuffer_len, &bibconns, dbires1 /* reference info */, item /* database */, ptr_biblio_info, ptr_clrequest->username, ptr_clrequest->pdfroot, nref_counter, title_as_author)) != 0) {
	send_status(ptr_clrequest->fd, n_status, TERM_NO);
	append_return_msg(ptr_addresult, n_status, id_string, bibconns.conn);
	ptr_addresult->failure++;
	goto FinishLoop;
      }
    }
    else {
      if ((n_status = render_dbib(&outbuffer, &outbuffer_len, &bibconns, dbires1 /* reference info */, item /* database */, ptr_biblio_info, ptr_clrequest->username, ptr_clrequest->pdfroot, nref_counter, title_as_author, ptr_clrequest->namespace, &xindent, n_ref_format)) != 0) {
	send_status(ptr_clrequest->fd, n_status, TERM_NO);
	append_return_msg(ptr_addresult, n_status, id_string, bibconns.conn);
	ptr_addresult->failure++;
	goto FinishLoop;
      }
    }

    /* run a character encoding conversion if required */
    if (conv_descriptor && *outbuffer) {
      size_t inlength;
      size_t outlength;
      size_t orig_outlength;
      char* my_outbuffer = NULL; /* this ptr will be modified by iconv() */
      char* my_outbuffer_start = NULL; /* records initial state of outbuffer */
      const char* my_instring = NULL; /* this ptr will be modified by iconv() */
      inlength = strlen(outbuffer);
      /* with the encodings supported by our database engines, the converted
	 string can't be longer than six times the input string */
      outlength = 6*inlength;
      orig_outlength = outlength;

      if ((my_outbuffer = malloc(outlength)) == NULL) {
	send_status(ptr_clrequest->fd, 801, TERM_NO);
	append_return_msg(ptr_addresult, 801, id_string, bibconns.conn);
	ptr_addresult->failure++;
	dbi_result_free(dbires1);
	dbires1 = NULL;
	dbi_result_free(dbires);
	dbires1 = NULL;
	retval = 1;
	goto Finish;
      }

      /* keep start of the converted string */
      my_outbuffer_start = my_outbuffer;
	
      /* variable will be modified by iconv, so don't use original */
      my_instring = (const char*)outbuffer;

      /* now actually do the conversion */
      if (iconv(conv_descriptor, &my_instring, &inlength, &my_outbuffer, &outlength) == (size_t)(-1)) {
	if (errno == EILSEQ) {
	  LOG_PRINT(LOG_WARNING, "iconv: invalid input character sequence");
	}
	else if (errno == E2BIG) {
	  LOG_PRINT(LOG_WARNING, "iconv: output buffer too small");
	}
	else if (errno == EINVAL) {
	  LOG_PRINT(LOG_WARNING, "iconv: incomplete input character");
	}
	  
	send_status(ptr_clrequest->fd, 702, TERM_NO);
	append_return_msg(ptr_addresult, 702, id_string, bibconns.conn);
	ptr_addresult->failure++;
	goto FinishLoop;
      }
      /* else: conversion went ok. We free the original string and replace
	 it with the converted copy */
      if (outbuffer) {
	free(outbuffer);
      }
      outbuffer = my_outbuffer_start;
      outbuffer_len = orig_outlength;
      result_len = (size_t)(my_outbuffer - my_outbuffer_start);
    }
    /* else: no conversion required */
    else {
      result_len = strlen(outbuffer);
    }

    /* send ok status, then the terminated result string */
    send_status(ptr_clrequest->fd, 404, TERM_NO);
    iwrite(ptr_clrequest->fd, outbuffer, result_len);
    iwrite(ptr_clrequest->fd, cs_term, TERM_LEN);

    ptr_addresult->success++;

    /* this label is targeted if something goes wrong with a particular
     reference, without having to abandon the whole formatting process */
  FinishLoop:

    /* reset buffer string */
    outbuffer[0] = '\0';
    delete_lilimem(&sentinel, "id_string");

    if (dbires1) {
      dbi_result_free(dbires1);
      dbires1 = NULL;
    }

    if (strcmp(my_dbi_conn_get_cap(bibconns.conn_source, "multiple_db"), "t")) {
      dbi_conn_close(bibconns.conn_source);
      bibconns.conn_source = NULL;
    }

    n_client_status = read_status(ptr_clrequest->fd);

    if (n_client_status) { /* client aborted */
      dbi_result_free(dbires);
      dbires = NULL;
      retval = 1;
      goto Finish;
    }
  } /* end loop over all distinct references */

  if (n_ref_format == REFRTF) {
    if ((n_status = finish_render_rtfbib(&outbuffer, &outbuffer_len, &bibconns, ptr_biblio_info, table_name, ptr_clrequest)) != 0) {
      send_status(ptr_clrequest->fd, n_status, TERM_NO);
      append_return_msg(ptr_addresult, n_status, NULL, bibconns.conn);
      dbi_result_free(dbires);
      dbires = NULL;
      retval = 1;
      goto Finish;
    }
  }
  else {
    if ((n_status = finish_render_dbib(&outbuffer, &outbuffer_len, &bibconns, ptr_biblio_info, table_name, ptr_clrequest, &xindent, n_ref_format)) != 0) {
      send_status(ptr_clrequest->fd, n_status, TERM_NO);
      append_return_msg(ptr_addresult, n_status, NULL, bibconns.conn);
      dbi_result_free(dbires);
      dbires = NULL;
      retval = 1;
      goto Finish;
    }
  }

  /* run a character encoding conversion if required */
  if (conv_descriptor && *outbuffer) {
    size_t inlength;
    size_t outlength;
    size_t orig_outlength;
    char* my_outbuffer = NULL; /* this ptr will be modified by iconv() */
    char* my_outbuffer_start = NULL; /* records initial state of outbuffer */
    const char* my_instring = NULL; /* this ptr will be modified by iconv() */
    inlength = strlen(outbuffer);
    /* with the encodings supported by our database engines, the converted
       string can't be longer than six times the input string */
    outlength = 6*inlength;
    orig_outlength = outlength;

    if ((my_outbuffer = malloc(outlength)) == NULL) {
      send_status(ptr_clrequest->fd, 801, TERM_NO);
      append_return_msg(ptr_addresult, 801, id_string, bibconns.conn);
      ptr_addresult->failure++;
      dbi_result_free(dbires);
      dbires = NULL;
      retval = 1;
      goto Finish;
    }

    /* keep start of the converted string */
    my_outbuffer_start = my_outbuffer;
	
    /* variable will be modified by iconv, so don't use original */
    my_instring = (const char*)outbuffer;

    /* now actually do the conversion */
    if (iconv(conv_descriptor, &my_instring, &inlength, &my_outbuffer, &outlength) == (size_t)(-1)) {
      if (errno == EILSEQ) {
	LOG_PRINT(LOG_WARNING, "iconv: invalid input character sequence");
      }
      else if (errno == E2BIG) {
	LOG_PRINT(LOG_WARNING, "iconv: output buffer too small");
      }
      else if (errno == EINVAL) {
	LOG_PRINT(LOG_WARNING, "iconv: incomplete input character");
      }
	  
      send_status(ptr_clrequest->fd, 702, TERM_NO);
      append_return_msg(ptr_addresult, 702, id_string, bibconns.conn);
      ptr_addresult->failure++;
      goto Finish;
    }
    /* else: conversion went ok. We free the original string and replace
       it with the converted copy */
    if (outbuffer) {
      free(outbuffer);
    }
    outbuffer = my_outbuffer_start;
    outbuffer_len = orig_outlength;
    result_len = (size_t)(my_outbuffer - my_outbuffer_start);
  }
  /* else: no conversion required */
  else {
    result_len = strlen(outbuffer);
  }



  /* send ok status, then the terminated result string */
  send_status(ptr_clrequest->fd, 402, TERM_NO);
  iwrite(ptr_clrequest->fd, outbuffer, result_len);
  n_writeresult = iwrite(ptr_clrequest->fd, cs_term, TERM_LEN);



/*   send_status(ptr_clrequest->fd, 402, TERM_NO); */
/*   n_writeresult = tiwrite(ptr_clrequest->fd, outbuffer, TERM_YES); */
  /* todo: test */
/*   printf("client sleeps for 5sec\n"); */
/*   sleep(5); */
  /* end test */
  /*  	printf("n_writeresult=%d<<\n", n_writeresult); */

  /* todo: switch on after debugging */

  /* remove the temporary table */
  sprintf(sql_command, "DROP TABLE %s", table_name);
  LOG_PRINT(LOG_DEBUG, sql_command);
  dbires1 = dbi_conn_query(bibconns.conn, sql_command);
  if (!dbires1) {
    append_return_msg(ptr_addresult, 243, NULL, bibconns.conn);
  }
  else {
    dbi_result_free(dbires1);
    dbires1 = NULL;
  }

  dbi_result_free(dbires);
  dbires = NULL;

 Finish:
  close_dbi_connections(&bibconns, drivername);
  
  ptr_addresult->success = nref_counter - ptr_biblio_info->n_startnumber + 1;

  if (ndb_notfound) {
    Lilifstring* ptr_curr;
    char buffer[260];
    char* msg_temp;

    if (!ptr_addresult->msg) {
      ptr_addresult->msg = malloc(256); /* something to start with */

      if (ptr_addresult->msg) {
	*(ptr_addresult->msg) = '\0';
	ptr_addresult->msg_len = 256;
      }
    }
    
    if (ptr_addresult->msg) {
      if ((msg_temp = mstrcat(ptr_addresult->msg, "Couldn't find these references:", &(ptr_addresult->msg_len), 0)) != NULL) {
	ptr_addresult->msg = msg_temp;
      }
      
      ptr_curr = &notfound_first;

      /* todo: write each ref on it's own line with a numerical prefix */
      while ((ptr_curr = get_next_lilifstring(ptr_curr))) {
	sprintf(buffer, " %s", ptr_curr->token);
	if ((msg_temp = mstrcat(ptr_addresult->msg, buffer, &(ptr_addresult->msg_len), 0)) == NULL) {
	  break;
	}
	else {
	  ptr_addresult->msg = msg_temp;
	}
      }
    }

    /* clean up linked list */
    delete_all_lilifstring(&notfound_first);

  } /* end if ndb_notfound */

  delete_all_lilimem(&sentinel);
  if (prev_authorconcat) {
    free(prev_authorconcat);
  }

  if (conv_descriptor) {
    iconv_close(conv_descriptor);
  }
/*   printf("retval went to %d, success went to %qu<<\n", retval, ptr_addresult->success); */

  return retval;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  close_dbi_connections(): closes open connections in getbib()

  void close_dbi_connections

  struct BIBCONNS ptr_bibconns ptr to structure with connections

  const char* drivername ptr to string containing the dbi driver name

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void close_dbi_connections(struct BIBCONNS* ptr_bibconns, const char* drivername) {
  if (ptr_bibconns->conn) {
    if (!strcmp(my_dbi_conn_get_cap(ptr_bibconns->conn, "multiple_db"), "t")) {
      dbi_conn_close(ptr_bibconns->conn);
    }
    else {
/*       printf("free conn\n"); */
      dbi_conn_close(ptr_bibconns->conn);
      if (ptr_bibconns->conn_refdb) {
	/*       printf("free conn_refdb\n"); */
	dbi_conn_close(ptr_bibconns->conn_refdb);
      }
      if (ptr_bibconns->conn_source) {
	/*       printf("free conn_source\n"); */
	dbi_conn_close(ptr_bibconns->conn_source);
      }
    }
  }
  return;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  sort_entries_asis(): sorts refentries in a temporary table according
                       to the original sequence in the document

  int sort_entries_asis returns 0 if ok, >0 if error

  dbi_conn conn dbi connection to the temporary table

  struct CLIENT_REQUEST* ptr_ptr_clrequest ptr to client request structure

  struct ADDRESULT* ptr_addresult ptr to structure that receives
                    success info

  const char* table_name ptr to string containing the name of the
                    temporary table to query

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int sort_entries_asis(dbi_conn conn, struct CLIENT_REQUEST* ptr_clrequest, struct ADDRESULT* ptr_addresult, const char* table_name) {
  char *id_string;
  char sql_command[256];
  char sql_command1[256];
  int n_mydistinct;
  unsigned long long n_id;
  dbi_result dbires;
  dbi_result dbires1;
  dbi_result dbires2;

  /* this is surprisingly the most difficult case. We have to sort by
     id, but then we can't use DISTINCT to filter out duplicates.
     Instead we retrieve a DISTINCT list of orig_ids and feed a second
     query with these. We retrieve all entries for each orig_id and sort
     them by id. The first of each will be assigned a sorted_pos value
     of 1 while all subsequent will be assigned 0. Finally we can
     retrieve all rows with a sorted_pos value greater 0 and sort this
     by id */

  sprintf(sql_command, "SELECT DISTINCT entry_id FROM %s", table_name);

  LOG_PRINT(LOG_DEBUG, sql_command);
  dbires = dbi_conn_query(conn, sql_command);
  if (!dbires) {
    append_return_msg(ptr_addresult, 234, NULL, conn);
    return 234;
  }

  /* loop over all distinct original IDs */
  while (dbi_result_next_row(dbires)) {
    id_string = my_dbi_result_get_string_copy(dbires, "entry_id");
    if (!id_string || !strcmp(id_string, "ERROR")) {
      append_return_msg(ptr_addresult, 234, NULL, conn);
      dbi_result_free(dbires);
      if (id_string) {
	free(id_string);
      }
      return 234;
    }

    sprintf(sql_command1, "SELECT id FROM %s where entry_id='%s' ORDER BY id ASC", table_name, id_string);
	
    LOG_PRINT(LOG_DEBUG, sql_command1);
    dbires1 = dbi_conn_query(conn, sql_command1);
    if (!dbires1) {
      append_return_msg(ptr_addresult, 234, NULL, conn);
      dbi_result_free(dbires);
      free(id_string);
      return 234;
    }
	  
    n_mydistinct = 1;
    while (dbi_result_next_row(dbires1)) {
      n_id = my_dbi_result_get_idval(dbires1, "id");

      /* set sorted_pos of the first copy to 1, of all
	 subsequent copies to zero */
      if (n_mydistinct) {
	sprintf(sql_command1, "UPDATE %s SET sorted_pos=1 WHERE id="ULLSPEC, table_name, (unsigned long long)n_id);
	n_mydistinct--;
      }
      else {
	sprintf(sql_command1, "UPDATE %s SET sorted_pos=0 WHERE id="ULLSPEC, table_name, (unsigned long long)n_id);
      }
      dbires2 = dbi_conn_query(conn, sql_command1);
      LOG_PRINT(LOG_DEBUG, sql_command1);
      if (!dbires2) {
	append_return_msg(ptr_addresult, 234, NULL, conn);
	dbi_result_free(dbires1);
	dbi_result_free(dbires);
	free(id_string);
	return 234;
      }
      dbi_result_free(dbires2);
    } /* end while sorted IDs */
    dbi_result_free(dbires1);
    free(id_string);
  } /* end while distinct original IDs */

  dbi_result_free(dbires);
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  fill_in_authoryear_info(): fills info for author/year citation styles
                       into a temporary table with reference info

  int fill_in_authoryear_info returns 0 if ok, >0 if error

  struct BIBCONNS* ptr_bibconns ptr to struct with connections

  dbi_result dbires result of a query containing all distinct references
                    of the temporary table

  struct CLIENT_REQUEST* ptr_clrequest ptr to client request structure

  struct ADDRESULT* ptr_addresult ptr to structure that receives
                    success info

  struct bibinfo* biblio_info ptr to structure containing info for
                    formatting entries

  const char* table_name ptr to string containing the name of the
                    temporary table to query

  struct mset* ptr_mset_sentinel ptr to bibliomset relation argument stack

  unsigned long long* nref_counter ptr to a reference counter

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format format of output type (REFXX)

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int fill_in_authoryear_info(struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_cit, struct CLIENT_REQUEST* ptr_clrequest, struct ADDRESULT* ptr_addresult, struct bibinfo* biblio_info, const char* table_name, char* pubtype, struct mset* ptr_mset_sentinel, unsigned long long* nref_counter, struct xmlindent* ptr_indent, int n_ref_format) {
  int n_pubyear;
  int unique_count;
  size_t authorlist_buffer_len = 0;
  size_t sql_command_len = 0;
  int n_status;
  short title_as_author;
  char *id_string;
  char *authorlist_buffer;
  char *sql_command;
  char *new_sql_command;
  char suffix[17] = "";
  const char *item;
  const char *drivername;
  dbi_result dbires1;
  dbi_result dbires2;
  dbi_result dbires3;

  drivername = dbi_driver_get_name(dbi_conn_get_driver(ptr_bibconns->conn));

  sql_command_len = 1024;
  sql_command = malloc(sql_command_len);
  if (sql_command == NULL) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 801;
  }

  /* loop over all distinct references to fill in the sorted_pos and
     the author_abbrevlist column */
  while (dbi_result_next_row(dbires)) {
    (*nref_counter)++; /* will start at 1 as is common in bibliographies */

    id_string = my_dbi_result_get_string_copy(dbires, "entry_id");
    if (!id_string || !strcmp(id_string, "ERROR")) {
      append_return_msg(ptr_addresult, 234, NULL, ptr_bibconns->conn);
      if (id_string) {
	free(id_string);
      }
      free(sql_command);
      return 234;
    }

    sprintf(sql_command, "UPDATE %s SET sorted_pos="ULLSPEC" WHERE sorted_pos>0 AND entry_id='%s'", table_name, (unsigned long long)(*nref_counter), id_string);
    LOG_PRINT(LOG_DEBUG, sql_command);
    dbires1 = dbi_conn_query(ptr_bibconns->conn, sql_command);
    if (!dbires1) {
      append_return_msg(ptr_addresult, 234, NULL, ptr_bibconns->conn);
      free(id_string);
      free(sql_command);
      return 1;
    }
    dbi_result_free(dbires1);

    /* retrieve author information and format as a subseq in-text citation, fill into column */
    biblio_info->n_refdb_id = my_dbi_result_get_idval(dbires, "orig_id");
    biblio_info->entry_id = id_string;
	  
    authorlist_buffer_len = 1;
    authorlist_buffer = malloc(authorlist_buffer_len);
    if (authorlist_buffer == NULL) {
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      free(id_string);
      free(sql_command);
      return 801;
    }

    authorlist_buffer[0] = '\0';
    item = dbi_result_get_string(dbires, "dbname");
/* 	  errflag = my_dbi_conn_error_flag(conn); */
/* 	  printf("errflag2=%d\n", errflag); */

    if (!strcmp(my_dbi_conn_get_cap(ptr_bibconns->conn, "multiple_db"), "t")) {
      ptr_bibconns->conn_source = ptr_bibconns->conn; /* make sure */
    }
    else {
      /* need a connection to the source database */
      ptr_bibconns->conn_source = connect_to_db(ptr_clrequest, item, 0);
      if (!ptr_bibconns->conn_source) {
	LOG_PRINT(LOG_WARNING, get_status_msg(204));
	free(id_string);
	free(authorlist_buffer);
	free(sql_command);
	return 204;
      }
    }

    title_as_author = dbi_result_get_short(dbires, "title_as_author");

    if ((n_status = format_entry(&authorlist_buffer, &authorlist_buffer_len, pubtype, ptr_bibconns, dbires_cit, NULL /* authorlist only */, item, ptr_clrequest->username, 0, ptr_mset_sentinel, biblio_info, title_as_author, ptr_clrequest->namespace, ptr_indent, n_ref_format, ptr_clrequest->pdfroot)) != 0) {
      append_return_msg(ptr_addresult, n_status, NULL, ptr_bibconns->conn);
      free(id_string);
      free(authorlist_buffer);
      free(sql_command);
      return n_status;
    }
/*     printf("authorlist_buffer:%s<< len:%d\n", authorlist_buffer, authorlist_buffer_len); */

    if (strcmp(my_dbi_conn_get_cap(ptr_bibconns->conn, "multiple_db"), "t")) {
      dbi_conn_close(ptr_bibconns->conn_source);
      ptr_bibconns->conn_source = NULL;
    }

    if (dbi_conn_quote_string(ptr_bibconns->conn, &authorlist_buffer) == 0) {
      append_return_msg(ptr_addresult, 801, NULL, ptr_bibconns->conn);
      free(id_string);
      free(authorlist_buffer);
      free(sql_command);
      return 801;
    }
    
    /* authorlist_buffer_len is no longer valid at this point */

    /* increase buffer size if necessary */
    if (strlen(authorlist_buffer)+256 > sql_command_len) {
      sql_command_len = strlen(authorlist_buffer)+256;
      new_sql_command = (char*)realloc(sql_command, sql_command_len);
      if (!new_sql_command) {
	append_return_msg(ptr_addresult, 801, NULL, NULL);
	free(id_string);
	free(authorlist_buffer);
	free(sql_command);
	return 801;
      }
      else {
	sql_command = new_sql_command;
      }
    }

    sprintf(sql_command, "UPDATE %s SET author_abbrevlist=%s WHERE entry_id='%s'", table_name, authorlist_buffer, biblio_info->entry_id);
    LOG_PRINT(LOG_DEBUG, sql_command);
    dbires1 = dbi_conn_query(ptr_bibconns->conn, sql_command);
    if (!dbires1) {
      append_return_msg(ptr_addresult, 241, NULL, ptr_bibconns->conn);
      free(id_string);
      free(authorlist_buffer);
      free(sql_command);
      return 241;
    }
    dbi_result_free(dbires1);
    free(id_string);
    free(authorlist_buffer);
  } /* end loop over all distinct references */
  

  /* select all unique author_abbrevlists */
  sprintf(sql_command, "SELECT DISTINCT author_abbrevlist,pubyear, sorted_pos FROM %s WHERE sorted_pos>0 ORDER BY sorted_pos", table_name);
  LOG_PRINT(LOG_DEBUG, sql_command);
  dbires1 = dbi_conn_query(ptr_bibconns->conn, sql_command);
  if (!dbires1) {
    append_return_msg(ptr_addresult, 234, NULL, ptr_bibconns->conn);
    free(sql_command);
    return 234;
  }

  /* loop over all abbrevlists */
  while (dbi_result_next_row(dbires1)) {
    item = my_dbi_result_get_string_copy(dbires1, "author_abbrevlist");
    n_pubyear = dbi_result_get_ushort(dbires1, "pubyear");

    /* quote item */
    if (dbi_conn_quote_string(ptr_bibconns->conn, (char**)&item) == 0) {
      append_return_msg(ptr_addresult, 241, NULL, ptr_bibconns->conn);
      free(sql_command);
      free((char*)item);
      return 241;
    }
    

    /* increase buffer size if necessary */
    if (strlen(item)+256 > sql_command_len) {
      sql_command_len = strlen(item)+256;
      new_sql_command = (char*)realloc(sql_command, sql_command_len);
      if (!new_sql_command) {
	append_return_msg(ptr_addresult, 801, NULL, NULL);
	dbi_result_free(dbires1);
	free(sql_command);
	free((char*)item);
	return 801;
      }
      else {
	sql_command = new_sql_command;
      }
    }

    sprintf(sql_command, "SELECT DISTINCT entry_id,sorted_pos FROM %s WHERE author_abbrevlist=%s AND pubyear=%d AND sorted_pos>0 ORDER BY sorted_pos", table_name, item, n_pubyear);
    LOG_PRINT(LOG_DEBUG, sql_command);

    free((char*)item);

    dbires2 = dbi_conn_query(ptr_bibconns->conn, sql_command);
    if (!dbires2) {
      append_return_msg(ptr_addresult, 234, NULL, ptr_bibconns->conn);
      dbi_result_free(dbires1);
      free(sql_command);
      return 234;
    }
    if (dbi_result_get_numrows(dbires2) > 1) { /* more than one entry with identical author/year combo */
      /* add a unique suffix to each entry */
      unique_count = 1; /* todo: was 0 */
      *suffix = '\0'; /* reset suffix to empty string */

      while (dbi_result_next_row(dbires2)) {
	if (unique_count > 0) {
	  if (increment_suffix(suffix, 16, 0)) {
	    append_return_msg(ptr_addresult, 804, NULL, ptr_bibconns->conn);
	    dbi_result_free(dbires2);
	    dbi_result_free(dbires1);
	    free(sql_command);
	    return 804;
	  }
/* 	  printf("suffix went to %s<<\n", suffix); */
	  id_string = my_dbi_result_get_string_copy(dbires2, "entry_id");
	  if (!id_string || !strcmp(id_string, "ERROR")) {
	    append_return_msg(ptr_addresult, 234, NULL, ptr_bibconns->conn);
	    dbi_result_free(dbires2);
	    dbi_result_free(dbires1);
	    if (id_string) {
	      free(id_string);
	    }
	    free(sql_command);
	    return 234;
	  }
	  sprintf(sql_command, "UPDATE %s SET year_uni_suffix='%s' WHERE entry_id='%s'", table_name, suffix, id_string);
	  LOG_PRINT(LOG_DEBUG, sql_command);
	  dbires3 = dbi_conn_query(ptr_bibconns->conn, sql_command);
	  if (!dbires3) {
	    append_return_msg(ptr_addresult, 241, NULL, ptr_bibconns->conn);
	    dbi_result_free(dbires2);
	    dbi_result_free(dbires1);
	    free(id_string);
	    free(sql_command);
	    return 241;
	  }
	  dbi_result_free(dbires3);
	  free(id_string);
	}
	unique_count++;
      }
    }
    dbi_result_free(dbires2);
  }
  dbi_result_free(dbires1);
  free(sql_command);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  getid(): retrieve id and database from an (uppercased) id string
           will also retrieve id for multiple citations
	   The id string format is: [dbname-]IDcitekey

  char* getid returns the id part of the string or NULL if the string
              is not a valid refdb citation string

  char* idstring the string to be examined

  char** db ptr to a string ptr. This will be filled in with a ptr to
         the database part of the idstring. This will be NULL if the
         string is invalid or if no database is specified

  int* type ptr to an int that will be set according to the id type
         0=regular citation 1=first in citation with multiple xrefs

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* getid(char* idstring, char** db, int* type) {
  char* start_refdb;
  char* start_id;
  int n_speclen; /* length of the ID or whatever string */

  *type = 0;

  /* try to find either "-ID", "ID", or "IM" */
  /* start with "-ID" to prevent finding database names containing "ID" */
  start_refdb = strstr(idstring, "-ID");
  if (start_refdb == NULL) {
    start_refdb = strstr(idstring, "ID");
    if (start_refdb == NULL) {
      /* the IM stuff is a free form link name that never carries a
	 database name, so looking for IM alone is sufficient */
      start_refdb = strstr(idstring, "IM");
      if (start_refdb == NULL) {
	*db = NULL;
	return NULL; /* not a refdb citation string */
      }
      else {
	(*type)++;
	n_speclen = 2;
      }
    }
    else {
      n_speclen = 2;
    }
  }
  else {
    n_speclen = 3;
  }

  if (start_refdb == idstring) {
    *db = NULL; /* no database specified (will use default)*/
    start_id = idstring+n_speclen;
  }
  else {
    *(start_refdb) = '\0';
    *db = idstring; /* database specified */
    start_id = start_refdb+n_speclen;
  }
  
/*   if (!*type) { */
    /* id sanity check will discard all non-numeric characters after ID */
/*     end_string = start_id + strlen(start_id); */
/*     stop_id = start_id; */

/*     while (stop_id <= end_string) { */
/*       if (!isdigit((int)(*stop_id))) { */
/* 	break; */
/*       } */
/*       stop_id++; */
/*     } */
/*     *stop_id = '\0'; */
/*   } */
  /* else do nothing: the multiple citation id may be non-numeric */

  if (!(*start_id)) {
    return NULL;
  }
  else {
    return start_id;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  get_bibtex_id(): retrieve id and database from a bibtex id string
	   The id string format is: [dbname:]citekey

  char* get_bibtex_id returns the id part of the string or NULL if
           the string is not a valid refdb citation string

  char* idstring the string to be examined

  char** db ptr to a string ptr. This will be filled in with a ptr to
         the database part of the idstring. This will be NULL if the
         string is invalid or if no database is specified

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* get_bibtex_id(char* idstring, char** db) {
  char* start_refdb;
  char* start_id;

  /* try to find a colon */
  start_refdb = strchr(idstring, (int)':');
  if (start_refdb == NULL) {
    /* no database part */
    *db = NULL;
    if (*idstring == '\0') {
      /* empty string, no id either */
      return NULL;
    }
    return idstring;
  }
  else {
    *(start_refdb) = '\0';
    *db = idstring; /* database specified */
    start_id = start_refdb+1;
    return start_id;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  addstyle(): implements the client command addstyle

  int addstyle returns 0 if ok, 1 if failed

  struct CLIENT_REQUEST* ptr_clrequest ptr to structure with client info

  struct ADDRESULT* addresult this structure will be filled in with the number
                      of (un-)successfully added/updated styles

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int addstyle(struct CLIENT_REQUEST* ptr_clrequest, struct ADDRESULT* ptr_addresult) {
  XML_Parser p;
  dbi_conn conn;
  char *return_msg = NULL; /* string to hold a return message for the client */
  int nmem_error;
  int ndb_error;
  int read_xml_res; /* result of read_xml */
  int depth; /* the current depth of the element stack */
  int depth_adjust; /* 0 if citestyle, 1 if styleset */
  int position; /* position of an element in PUBTYPE or INTEXTDEF */
  int set_count; /* number of finished CITESTYLE elements */
  unsigned int citstyle_id; /* id of the current CITSTYLE element */
  unsigned int refstyle_id; /* id of the current REFSTYLE element */
  unsigned int separator_id; /* id of the current SEPARATORS element */
  struct elstack *ptr_first; /* start of the element stack */
  char the_journal[256]; /* current journal name */
  
  struct addstyle_data asdata;

  /* get some memory for a return message */
  return_msg = malloc((size_t)256);
  if (return_msg == NULL) {
    /* out of memory */
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 1;
  }

  /* connect to database server*/
  if ((conn = connect_to_db(ptr_clrequest, NULL, 0)) == NULL) {
    /* access denied to reference database */
    LOG_PRINT(LOG_WARNING, get_status_msg(204));
    send_status(ptr_clrequest->fd, 204, TERM_NO);
    free(return_msg);
    return 1;
  }
  
  /* this global should not be reset in the loop */
  set_count = 0;

  send_status(ptr_clrequest->fd, 0, TERM_NO);

  while (1) { /* leave with return or break */
    /* initialize "globals" */
    nmem_error = 0;
    ndb_error = 0;
    citstyle_id = 0;
    refstyle_id = 0;
    separator_id = 0;
    ptr_first = NULL;
    depth_adjust = 0;
    depth = 0;
    position = 0;

    asdata.ptr_nmem_error = &nmem_error;
    asdata.ptr_ndb_error = &ndb_error;
    asdata.ptr_depth = &depth;
    asdata.ptr_depth_adjust = &depth_adjust;
    asdata.ptr_position = &position;
    asdata.ptr_set_count = &set_count;
    asdata.ptr_citstyle_id = &citstyle_id;
    asdata.ptr_refstyle_id = &refstyle_id;
    asdata.ptr_separator_id = &separator_id;
    asdata.ptr_first = ptr_first;
    asdata.the_journal = the_journal;
    asdata.conn = conn;
    asdata.ptr_addresult = ptr_addresult;

    /* create the parser instance */
    p = XML_ParserCreate(NULL);
    if (!p) {
      /* out of memory */
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      send_status(ptr_clrequest->fd, 801, TERM_NO);
      dbi_conn_close(conn);
      free(return_msg);
      return 2;
    }

    /* register our handlers. these handlers will be called whenever expat finds a start- or endtag or character data */
    XML_SetElementHandler(p, start_handler, end_handler);
    XML_SetCharacterDataHandler(p, char_handler);

    /* make pointer to "global data" available for handlers */
    XML_SetUserData(p, (void*)&asdata);

    read_xml_res = read_xml(ptr_clrequest->fd, p, ptr_addresult);
    XML_ParserFree(p);
    if (read_xml_res == 0
	|| read_xml_res == 2) { /* error */
      delete_list(ptr_first);
      ptr_addresult->failure += 1;

      /* try to remove the data that already went into the db */
      remove_style(conn, citstyle_id);
      if (read_xml_res == 0) {
	break;
      }
      /* else: try next */
/*       dbi_conn_close(conn); */
/*       return 2; */
    }
    else if (read_xml_res == -1) { /* we're done */
      break;
    }
  }

  /* send report to client */
/*   send_status(ptr_clrequest->fd, 0, TERM_NO); */
  tiwrite(ptr_clrequest->fd, ptr_addresult->msg, TERM_YES);

  ptr_addresult->success = set_count;
  dbi_conn_close(conn);
  free(return_msg);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  deletestyle(): implements the client command deletestyle

  int deletestyle returns >0 if failed, 0 if successful

  struct CLIENT_REQUEST* ptr_clrequest ptr to structure with client info

  struct ADDRESULT* addresult this structure will be filled in with the number
                      of (un-)successfully deleted styles

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
int deletestyle(struct CLIENT_REQUEST* ptr_clrequest, struct ADDRESULT* ptr_addresult) {
  dbi_conn conn;
  dbi_result dbires;
  dbi_driver driver;
  unsigned int n_id;
  char* sql_command = NULL;
  char* myarg;
  char* new_msg;
  struct lilimem sentinel;

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


  /* get some memory for sql commands */
  sql_command = malloc((size_t)512);
  if (sql_command == NULL || insert_lilimem(&sentinel, (void**)&sql_command, NULL)) {
    delete_all_lilimem(&sentinel);
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 1;
  }

  /* connect to database server*/
  if ((conn = connect_to_db(ptr_clrequest, NULL, 0)) == NULL) {
/*      printf("connect_to_db failed\n"); */
    send_status(ptr_clrequest->fd, 202, TERM_NO);
    LOG_PRINT(LOG_WARNING, get_status_msg(202));
    delete_all_lilimem(&sentinel);
    return 1;
  }

  driver = dbi_conn_get_driver(conn);

  myarg = strdup(ptr_clrequest->argument);
  if (!myarg || insert_lilimem(&sentinel, (void**)&myarg, NULL)) {
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    delete_all_lilimem(&sentinel);
    dbi_conn_close(conn);
    return 1;
  }

  if (dbi_conn_quote_string(conn, &myarg) == 0) {
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    delete_all_lilimem(&sentinel);
    dbi_conn_close(conn);
    return 1;
  }

  sprintf(sql_command, "SELECT ID,JOURNAL FROM CITSTYLE WHERE JOURNAL %s %s", my_dbi_conn_get_cap(conn, "rlike"), myarg);
  dbires = dbi_conn_query(conn, sql_command);
  LOG_PRINT(LOG_DEBUG, sql_command);
  if (!dbires) {
    send_status(ptr_clrequest->fd, 234, TERM_NO);
    LOG_PRINT(LOG_WARNING, get_status_msg(234));
    delete_all_lilimem(&sentinel);
    dbi_conn_close(conn);
    return 1;
  }

  *sql_command = '\0';

  if (!dbi_result_get_numrows(dbires)) {
    sprintf(sql_command, "417:%s\n", ptr_clrequest->argument);
    ptr_addresult->skipped++;
    
    if ((new_msg = mstrcat(ptr_addresult->msg, sql_command, &(ptr_addresult->msg_len), 0)) == NULL) {
      send_status(ptr_clrequest->fd, 801, TERM_NO);
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      delete_all_lilimem(&sentinel);
      dbi_conn_close(conn);
      return 1;
    }
    else {
      ptr_addresult->msg = new_msg;
    }
  }
  else {
    while (dbi_result_next_row(dbires)) {
      char *stylename;

      n_id = my_dbi_result_get_int_idval(dbires, "ID");
      stylename = my_dbi_result_get_string_copy(dbires, "JOURNAL");
      if (!remove_style(conn, n_id)) {
	sprintf(sql_command, "419:%u:%s\n", n_id, (stylename) ? stylename:"");
	ptr_addresult->success++;
      }
      else {
	sprintf(sql_command, "420:%u:%s\n", n_id, (stylename) ? stylename:"");
	ptr_addresult->failure++;
      }

      if (stylename) {
	free(stylename);
      }
      
      if ((new_msg = mstrcat(ptr_addresult->msg, sql_command, &(ptr_addresult->msg_len), 0)) == NULL) {
	send_status(ptr_clrequest->fd, 801, TERM_NO);
	LOG_PRINT(LOG_CRIT, get_status_msg(801));
	delete_all_lilimem(&sentinel);
	dbi_conn_close(conn);
	return 1;
      }
      else {
	ptr_addresult->msg = new_msg;
      }
    }
  }

  /* send back report to client */
  send_status(ptr_clrequest->fd, 0, TERM_NO);
  tiwrite(ptr_clrequest->fd, ptr_addresult->msg, TERM_YES);

  dbi_conn_close(conn);
  delete_all_lilimem(&sentinel);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  liststyle(): implements the client command liststyle

  int liststyle returns 0 if ok, >0 if failed

  struct CLIENT_REQUEST* ptr_clrequest ptr to structure with client info

  struct ADDRESULT* addresult structure with counters
  

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int liststyle(struct CLIENT_REQUEST* ptr_clrequest, struct ADDRESULT* ptr_addresult) {
  char sql_command[256];
  const char* journal;
  const char* drivername;
  char *myarg;
  dbi_conn conn;
  dbi_result dbires;
  dbi_driver driver;

  /* connect to the database */
  if ((conn = connect_to_db(ptr_clrequest, main_db, 0)) != NULL) {
    driver = dbi_conn_get_driver(conn);

    drivername = dbi_driver_get_name(dbi_conn_get_driver(conn));

    if (ptr_clrequest->argument && *(ptr_clrequest->argument)) {
      myarg = strdup(ptr_clrequest->argument);
    }
    else {
      myarg = strdup(my_dbi_driver_get_cap(driver, "listall")); /* list all if no argument */
    }

    if (!myarg) {
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      send_status(ptr_clrequest->fd, 801, TERM_NO);
      return 1;
    }

    if (dbi_conn_quote_string(conn, &myarg) == 0) {
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      send_status(ptr_clrequest->fd, 801, TERM_NO);
      dbi_conn_close(conn);
      return 1;
    }

    sprintf(sql_command, "SELECT JOURNAL FROM CITSTYLE WHERE JOURNAL %s %s", my_dbi_conn_get_cap(conn, "rlike"), myarg);

    free(myarg);

    dbires = dbi_conn_query(conn, sql_command);
    LOG_PRINT(LOG_DEBUG, sql_command);
    if (dbires) {
      send_status(ptr_clrequest->fd, 0, TERM_NO);
      while (dbi_result_next_row(dbires)) {
	journal = my_dbi_result_get_string_idx(dbires, 1);
	if (journal) {
	  tiwrite(ptr_clrequest->fd, journal, TERM_NO);
	  tiwrite(ptr_clrequest->fd, "\n", TERM_NO);
	  ptr_addresult->success++;
	}
	else {
	  ptr_addresult->failure++;
	}
      }
      dbi_result_free(dbires);
      tiwrite(ptr_clrequest->fd, "", TERM_YES);
    }
    dbi_conn_close(conn);
  }
  else {
      LOG_PRINT(LOG_WARNING, get_status_msg(202));
      send_status(ptr_clrequest->fd, 202, TERM_NO);
      return 1;
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  getstyle(): implements the client command getstyle

  int getstyle returns 0 if ok, 1 if mem error, 2 if other error

  struct CLIENT_REQUEST* ptr_clrequest ptr to structure with client info

  struct ADDRESULT* ptr_addresult ptr to struct which receives the number
                    of styles

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int getstyle(struct CLIENT_REQUEST* ptr_clrequest, struct ADDRESULT* ptr_addresult) {
  char header[80];
  char *sql_command;
  const char *encoding;
  dbi_conn conn;
  dbi_result dbires;
  dbi_result dbires_ref;
  dbi_driver driver;
  int n_have_styleset = 0;
  unsigned int citstyle_id;
  unsigned int refstyle_id;

  Lilimem sentinel;
  Liliform attr_sentinel;

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

  attr_sentinel.ptr_next = NULL;
  attr_sentinel.name[0] = '\0';
  attr_sentinel.value = NULL;

  if ((sql_command = malloc(1024)) == NULL
      || insert_lilimem(&sentinel, (void**)&sql_command, NULL)) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    send_status(ptr_clrequest->fd, 801, TERM_NO);
    return 1;
  }

  /* strategy to get styles:
     - write header with container start markup
     - loop over all journal names
     - write style info
     - loop over all publication types
     - write style info
     - write citstyle info
     - write footer with container end markup
  */

  send_status(ptr_clrequest->fd, 000, TERM_NO);

  /* connect to main database */
  if ((conn = connect_to_db(ptr_clrequest, main_db, 0)) == NULL) {
    LOG_PRINT(LOG_WARNING, get_status_msg(202));
    return 1;
  }
  driver = dbi_conn_get_driver(conn);

  /* assemble processing instruction */
  encoding = dbi_conn_get_encoding(conn);
  snprintf(header, 80, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", encoding);

  /* write processing instruction */
  if (tiwrite(ptr_clrequest->fd, header, TERM_NO) == -1) {
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    delete_all_lilimem(&sentinel);
    dbi_conn_close(conn);
    return 1;
  }

  /* write doctype line */
  if (ptr_clrequest->inargc - *(ptr_clrequest->ptr_optind) > 0) { /* write STYLESET element*/
    n_have_styleset++;
    if (tiwrite(ptr_clrequest->fd, DOCTYPE_STYLESET, TERM_NO) == -1) {
      LOG_PRINT(LOG_WARNING, get_status_msg(110));
      delete_all_lilimem(&sentinel);
      dbi_conn_close(conn);
      return 2;
    }
    iwrite_elstart(ptr_clrequest, "STYLESET", NULL, 0);
  }
  else { /* no tokens found */
    LOG_PRINT(LOG_WARNING, get_status_msg(111));
    delete_all_lilimem(&sentinel);
    dbi_conn_close(conn);
    return 2;
  }

  /* loop over all requested styles */
  for (; *(ptr_clrequest->ptr_optind) < ptr_clrequest->inargc; (*(ptr_clrequest->ptr_optind))++) {
    sprintf(sql_command, "SELECT ID,JOURNAL,AUTHOR,COMMENT,URL,CITSEPARATOR,FOLLOWING,PRECEEDING,RANGESEPARATOR,BIBLIOTITLE,INTEXTSEQUENCE,BIBLIOSEQUENCE,BIBLIOFIRSTINDENT,BIBLIOBLOCKINDENT,FONTSIZE,STYLE,JANABBREV,FEBABBREV,MARABBREV,APRABBREV,MAYABBREV,JUNABBREV,JULABBREV,AUGABBREV,SEPABBREV,OCTABBREV,NOVABBREV,DECABBREV,JANFULL,FEBFULL,MARFULL,APRFULL,MAYFULL,JUNFULL,JULFULL,AUGFULL,SEPFULL,OCTFULL,NOVFULL,DECFULL,JANTHREELET,FEBTHREELET,MARTHREELET,APRTHREELET,MAYTHREELET,JUNTHREELET,JULTHREELET,AUGTHREELET,SEPTHREELET,OCTTHREELET,NOVTHREELET,DECTHREELET FROM CITSTYLE WHERE JOURNAL='%s'", (ptr_clrequest->inargv)[*(ptr_clrequest->ptr_optind)]);

    dbires = dbi_conn_query(conn, sql_command);
    LOG_PRINT(LOG_DEBUG, sql_command);
    if (dbires) {
      while (dbi_result_next_row(dbires)) { /* should run only once */
	citstyle_id = my_dbi_result_get_int_idval(dbires, "ID");

	/* start citestyle */
	iwrite_elstart(ptr_clrequest, "CITESTYLE", NULL, 0);

	/* output style name */
	iwrite_element(ptr_clrequest, "STYLENAME", NULL, my_dbi_result_get_string(dbires, "JOURNAL"));

	/* output style author */
	iwrite_element(ptr_clrequest, "AUTHOR", NULL, my_dbi_result_get_string(dbires, "AUTHOR"));

	/* output style comment */
	iwrite_element(ptr_clrequest, "COMMENT", NULL, my_dbi_result_get_string(dbires, "COMMENT"));

	/* output style url */
	iwrite_element(ptr_clrequest, "URL", NULL, my_dbi_result_get_string(dbires, "URL"));

	/* start refstyle block */
	iwrite_elstart(ptr_clrequest, "REFSTYLE", NULL, 0);

	/* loop over all publication types */
	sprintf(sql_command, "SELECT ID,PUBTYPE FROM REFSTYLE WHERE CITSTYLEID=%u AND PUBTYPE!=\'INTEXT\' AND PUBTYPE!=\'AUTHORONLY\' AND PUBTYPE!=\'YEARONLY\'", citstyle_id);
	dbires_ref = dbi_conn_query(conn, sql_command);
	LOG_PRINT(LOG_DEBUG, sql_command);
	if (dbires_ref) {
	  while (dbi_result_next_row(dbires_ref)) {
	    refstyle_id = my_dbi_result_get_int_idval(dbires_ref, "ID");
	      
	    iwrite_pubtype(ptr_clrequest, my_dbi_result_get_string(dbires_ref, "PUBTYPE"), citstyle_id, conn);
	  }
	}
	dbi_result_free(dbires_ref);

	/* end refstyle block */
	iwrite_elend(ptr_clrequest, "REFSTYLE");

	/* begin citstyle block */
	if (insert_liliform(&attr_sentinel, "INTEXTSEQUENCE", (char*)my_dbi_result_get_string(dbires, "INTEXTSEQUENCE"))) {
	  LOG_PRINT(LOG_CRIT, get_status_msg(801));
	  ptr_addresult->failure++;
	  break;
	}
	if (insert_liliform(&attr_sentinel, "STYLE", (char*)my_dbi_result_get_string(dbires, "STYLE"))) {
	  LOG_PRINT(LOG_CRIT, get_status_msg(801));
	  delete_all_liliform(&attr_sentinel);
	  ptr_addresult->failure++;
	  break;
	}

	iwrite_elstart(ptr_clrequest, "CITSTYLE", &attr_sentinel, 0);
	delete_all_liliform(&attr_sentinel);

	/* preceeding */
	iwrite_element(ptr_clrequest, "PRECEEDING", NULL, my_dbi_result_get_string(dbires, "PRECEEDING"));

	/* following */
	iwrite_element(ptr_clrequest, "FOLLOWING", NULL, my_dbi_result_get_string(dbires, "FOLLOWING"));

	/* citseparator */
	iwrite_element(ptr_clrequest, "CITSEPARATOR", NULL, my_dbi_result_get_string(dbires, "CITSEPARATOR"));

	/* rangeseparator */
	iwrite_element(ptr_clrequest, "RANGESEPARATOR", NULL, my_dbi_result_get_string(dbires, "RANGESEPARATOR"));

	/* intextdef */
	iwrite_pubtype(ptr_clrequest, "INTEXTDEF", citstyle_id, conn);

	/* authoronly */
	iwrite_pubtype(ptr_clrequest, "AUTHORONLY", citstyle_id, conn);

	/* yearonly */
	iwrite_pubtype(ptr_clrequest, "YEARONLY", citstyle_id, conn);

	/* end citstyle block */
	iwrite_elend(ptr_clrequest, "CITSTYLE");

	/* begin bibstyle block */
	if (insert_liliform(&attr_sentinel, "BIBLIOSEQUENCE", (char*)my_dbi_result_get_string(dbires, "BIBLIOSEQUENCE"))) {
	  LOG_PRINT(LOG_CRIT, get_status_msg(801));
	  delete_all_liliform(&attr_sentinel);
	  ptr_addresult->failure++;
	  break;
	}

	if (insert_liliform(&attr_sentinel, "BIBLIOFIRSTINDENT", (char*)my_dbi_result_get_string(dbires, "BIBLIOFIRSTINDENT"))) {
	  LOG_PRINT(LOG_CRIT, get_status_msg(801));
	  delete_all_liliform(&attr_sentinel);
	  ptr_addresult->failure++;
	  break;
	}

	if (insert_liliform(&attr_sentinel, "BIBLIOBLOCKINDENT", (char*)my_dbi_result_get_string(dbires, "BIBLIOBLOCKINDENT"))) {
	  LOG_PRINT(LOG_CRIT, get_status_msg(801));
	  delete_all_liliform(&attr_sentinel);
	  ptr_addresult->failure++;
	  break;
	}

	if (insert_liliform(&attr_sentinel, "FONTSIZE", (char*)my_dbi_result_get_string(dbires, "FONTSIZE"))) {
	  LOG_PRINT(LOG_CRIT, get_status_msg(801));
	  delete_all_liliform(&attr_sentinel);
	  ptr_addresult->failure++;
	  break;
	}

	iwrite_elstart(ptr_clrequest, "BIBSTYLE", &attr_sentinel, 0);
	delete_all_liliform(&attr_sentinel);

	/* bibliotitle */
	iwrite_element(ptr_clrequest, "BIBLIOTITLE", NULL, my_dbi_result_get_string(dbires, "BIBLIOTITLE"));

	/* months block */
	iwrite_months(ptr_clrequest, dbires);

	/* end bibstyle block */
	iwrite_elend(ptr_clrequest, "BIBSTYLE");

	/* end citestyle */
	iwrite_elend(ptr_clrequest, "CITESTYLE");
      }
      dbi_result_free(dbires);
      ptr_addresult->success++;
    } /* end if style exists */
    else {
      ptr_addresult->skipped++;
    }
  } /* end for loop over all tokens */

  dbi_conn_close(conn);
    
  if (n_have_styleset) {
    tiwrite(ptr_clrequest->fd, "</STYLESET>\n", TERM_YES);
  }

  delete_all_lilimem(&sentinel);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  read_xml() reads XML data from a client connection and sends them 
             to an XML parser

  int read_xml returns 0 if failed, 1 if ok, -1 if ok and the client
             signals that no more files will be sent, 2 if failed but
	     more files are to come

  int fd file descriptor for output

  XML_Parser p the XML parser

  struct ADDRESULT* ptr_addresult pointer to a structure that will
            receive some result information

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int read_xml(int fd, XML_Parser p, struct ADDRESULT* ptr_addresult) {
  int numbyte;
  int n_requested_bufsize;
  int n_parse_error = 0;
  int chunk_count = 0;
  int cs_status;
  char return_msg[256];
  char inbuffer[COMMAND_INBUF_LEN]; /* buffer for client commands */
  char* style_set = NULL;
  char* new_style_set;

  style_set = malloc((size_t)1);
  if (style_set == NULL) {
    /* out of memory */
    send_status(fd, 801, TERM_NO);
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 0;
  }

  /* loop until the client requests a buffer size of zero indicating
     that he's done sending datasets */
  do {
    /* ------------------------------------------------------------ */
    /* PHASE 1 */
    /* see how much memory we'll need */
    cs_status = read_status(fd);
    if (cs_status == 402 || cs_status == 404) {
      /* dataset is done, let parser finish */
      LOG_PRINT(LOG_INFO, get_status_msg(cs_status));
      if (! XML_Parse(p, style_set, 0, 1)) {
	send_status(fd, 400, TERM_NO);
	LOG_PRINT(LOG_WARNING, "XML parse error");
	sprintf(return_msg, "Parse error at line "ULLSPEC":\n%s\n", (unsigned long long)XML_GetCurrentLineNumber(p), XML_ErrorString(XML_GetErrorCode(p)));
	tiwrite(fd, return_msg, TERM_YES);

	if (cs_status == 404) {
	  return 2;
	}
	else {
	  return 0;
	}
      }
      free(style_set);
      send_status(fd, 403, TERM_NO);
      LOG_PRINT(LOG_INFO, get_status_msg(403));

      if (cs_status == 404) {
	return 1; /* dataset is done, but there's more to come */
      }
      else { /* 402 */
	return -1; /* last dataset is done */
      }
    }
    else if (cs_status) {
      LOG_PRINT(LOG_WARNING, get_status_msg(cs_status));
      free(style_set);
      return 0;
    }

    /* client sends the number of bytes he wants to transmit */
    numbyte = tread(fd, inbuffer, 10);
    if (numbyte == -1) {
      LOG_PRINT(LOG_WARNING, get_status_msg(109));
      free(style_set);
      return 0;
    }
    
    /* the size of the chunk the client wants to send */
    n_requested_bufsize = atoi(inbuffer);
/*      printf("inbuffer: %s\nbufsize: %d\n", inbuffer, n_requested_bufsize); */

    /* ------------------------------------------------------------ */
    /* PHASE 2 */
    /* send back reply */

    /* try to get a buffer large enough to hold the reference
       if n_requested_bufsize == 0, then this command equals free() */
    /* do explicit free() to allow debugging with efence */
    if (n_requested_bufsize) {
      new_style_set = (char*)realloc(style_set, (size_t)n_requested_bufsize);
    }
    else {
      free(style_set);
      new_style_set = NULL;
    }
    if (new_style_set == NULL && n_requested_bufsize) {
      send_status(fd, 801, TERM_NO);
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      free(style_set);
      return 0;
    }
    else {
      style_set = new_style_set;
    }

    /* read the data proper unless size was zero (= end of transmission) */
    if (n_requested_bufsize) {
      /* acknowledge that we have a buffer ready, let them data come... */
      send_status(fd, 0, TERM_NO);

      /* ------------------------------------------------------------ */
      /* PHASE 3 */
      /* read dataset */
      numbyte = iread(fd, style_set, n_requested_bufsize);
/*       printf("requested: %d\nread: %d\n%c%c%c%c%c\n", n_requested_bufsize, numbyte, style_set[0], style_set[1], style_set[2], style_set[3], style_set[4]); */
      if (numbyte == -1) {
	LOG_PRINT(LOG_WARNING, get_status_msg(109));
	free(style_set);
	return 0;
      }

      /* ------------------------------------------------------------ */
      /* PHASE 4 */
      /* send back message */

      /* split style into elements and squeeze contents into database */
      if (! XML_Parse(p, style_set, numbyte, 0)) {
	send_status(fd, 400, TERM_NO);
	LOG_PRINT(LOG_WARNING, "XML parse error");
	sprintf(return_msg, "Parse error at line "ULLSPEC" column "ULLSPEC":\n%s\n", (unsigned long long)XML_GetCurrentLineNumber(p), (unsigned long long)XML_GetCurrentColumnNumber(p), XML_ErrorString(XML_GetErrorCode(p)));
	tiwrite(fd, return_msg, TERM_YES);
	ptr_addresult->failure++;
	free(style_set);
	return 0;
      }
      else {
/*  	ptr_addresult->success++; */
	send_status(fd, 403, TERM_NO);
	LOG_PRINT(LOG_INFO, get_status_msg(403));
      }
      chunk_count++;
    }
    else { /* client is done sending data, let parser close */
      if (! XML_Parse(p, style_set, 0, 1)) {
	send_status(fd, 400, TERM_NO);
	LOG_PRINT(LOG_WARNING, "XML parse error");
	sprintf(return_msg, "Parse error at line "ULLSPEC":\n%s\n", (unsigned long long)XML_GetCurrentLineNumber(p), XML_ErrorString(XML_GetErrorCode(p)));
	tiwrite(fd, return_msg, TERM_YES);
	n_parse_error = 1;
      }
    }
  } while (n_requested_bufsize);
  
  if (style_set) {
    free(style_set);
  }

  if (n_parse_error) {
    return 0;
  }
  else {
    return 1;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  remove_style(): removes a style specification from the database

  int remove_style returns 0 if successful, >0 in case of an error

  dbi_conn conn ptr to a structure holding the dbi connection
                        information

  unsigned int n_id  the ID in CITSTYLE which is to be removed

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int remove_style(dbi_conn conn, unsigned int n_id) {
  char sql_command[256];
  int error_count = 0;
  unsigned int n_id1;
  unsigned int n_id2;
  dbi_result dbires;
  dbi_result dbires1;
  dbi_result dbires2;

  if (!n_id) {
    return 1;
  }

  sprintf(sql_command, "SELECT ID FROM REFSTYLE WHERE CITSTYLEID=%u", n_id);
  dbires = dbi_conn_query(conn, sql_command);
  LOG_PRINT(LOG_DEBUG, sql_command);
  if (!dbires) {
    return 1;
  }
  while (dbi_result_next_row(dbires)) {
    n_id1 = my_dbi_result_get_int_idval(dbires, "ID");
    if (!n_id1) {
      error_count++;
      dbi_result_free(dbires);
      continue;
    }

    sprintf(sql_command, "SELECT SEPARATORS.ID FROM SEPARATORS INNER JOIN POSITIONS ON POSITIONS.SEPARATORID=SEPARATORS.ID WHERE POSITIONS.REFSTYLEID=%u", n_id1);
    dbires1 = dbi_conn_query(conn, sql_command);
    LOG_PRINT(LOG_DEBUG, sql_command);
    if (!dbires1) {
      error_count++;
      dbi_result_free(dbires);
      continue;
    }

    while (dbi_result_next_row(dbires1)) {
      n_id2 = my_dbi_result_get_int_idval_idx(dbires1, 1);
      if (!n_id2) {
	error_count++;
	dbi_result_free(dbires);
	continue;
      }

      sprintf(sql_command, "DELETE FROM SEPARATORS WHERE ID=%u", n_id2);
      dbires2 = dbi_conn_query(conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires2) {
	error_count++;
      }
      else {
	dbi_result_free(dbires2);
      }
    }

    sprintf(sql_command, "DELETE FROM POSITIONS WHERE REFSTYLEID=%u", n_id1);
    dbires2 = dbi_conn_query(conn, sql_command);
    LOG_PRINT(LOG_DEBUG, sql_command);
    if (!dbires2) {
      error_count++;
    }
    dbi_result_free(dbires2);

    sprintf(sql_command, "DELETE FROM REFSTYLE WHERE ID=%u", n_id1);
    dbires2 = dbi_conn_query(conn, sql_command);
    LOG_PRINT(LOG_DEBUG, sql_command);
    if (!dbires2) {
      error_count++;
    }
    dbi_result_free(dbires2);
    dbi_result_free(dbires1);
  }

  sprintf(sql_command, "DELETE FROM CITSTYLE WHERE ID=%u", n_id);
  dbires1 = dbi_conn_query(conn, sql_command);
  LOG_PRINT(LOG_DEBUG, sql_command);
  if (!dbires1) {
    error_count++;
  }
  dbi_result_free(dbires1);

  dbi_result_free(dbires);
/*    printf("done remove_style()\n"); */
  return error_count;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  check_is_subseq(): checks whether a citation is the first or a subsequent
                     in the document

  int check_is_subseq returns -1 in case of an error, 0 if first, 1 if subsequent

  const char* author_concat ptr to the author_concat

  const char* table_name name of the table that contains author_concat data

  int n_currpos current position of citation

  int n_pubyear publication year

  dbi_conn conn ptr to an existing connection to the refdb database

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int check_is_subseq(const char* author_concat, const char* table_name, int n_currpos, short int n_pubyear, dbi_conn conn) {
  char* author_concat_q;
  char* sql_command;
  int n_is_subseq = 0;
  dbi_result dbires;

  if (!author_concat || !*author_concat) {
    /* nothing to do */
    return 0;
  }

  if ((author_concat_q = strdup(author_concat)) == NULL || dbi_conn_quote_string(conn, &author_concat_q) == 0) {
    return -1;
  }

  if ((sql_command = malloc(strlen(author_concat_q)+256)) == NULL) {
    free(author_concat_q);
    return -1;
  }

  sprintf(sql_command, "SELECT id FROM %s WHERE sorted_pos>0 AND sorted_pos < %d AND author_concat=%s AND pubyear=%d", table_name, n_currpos, author_concat_q, n_pubyear);

  free(author_concat_q);

  LOG_PRINT(LOG_DEBUG, sql_command);
  dbires = dbi_conn_query(conn, sql_command);
  free(sql_command);
  
  if (!dbires) {
    return -1;
  }

  if (dbi_result_get_numrows(dbires)) {
    n_is_subseq = 1;
  }
  else {
    n_is_subseq = 0;
  }
  dbi_result_free(dbires);

  return n_is_subseq;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  send_stylespec(): sends a style specification to the client; the
                    DSSSL driver file contains code for both html
		    and print; the xsl driver file needs postprocessing
		    to insert the proper path to a stylesheet

  static int send_stylespec returns 0 if successful, >0 if error

  int fd file descriptor for output

  dbi_conn conn ptr to an existing connection to the refdb database

  char* format_string name of the style to retrieve (properly quoted please)

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int send_stylespec(int fd, dbi_conn conn, char* format_string, int n_ref_format) {
  dbi_result dbires_ref;
  dbi_result dbires_cit;
  int numbyte; /* number of bytes read or written */
  unsigned int i; /* guess what */
  int haverow;
  unsigned int n_id;
  size_t sql_command_len = 2048;
  char *sql_command; /* buffer for assembling SQL commands */
  char outbuffer[512]; /* buffer for assembling output */
  const char *item;
  const char *item_pubtype;
  const char *drivername;
  const char *bibliofirstindent;
  const char *biblioblockindent;
  const char *fontsize;

  /* this array of strings contains part of the variable names in the output file (the column names in the database) */
  char tags[][50] = {
    "AUTHORLISTSTYLE",
    "EDITORLISTSTYLE",
    "SEDITORLISTSTYLE",
    "ALLALISTSTYLE",
    "AUTHORLISTALTERNATESTYLE",
    "EDITORLISTALTERNATESTYLE",
    "SEDITORLISTALTERNATESTYLE",
    "ALLALISTALTERNATESTYLE",
    "PUBDATESTYLE",
    "PUBDATESECSTYLE",
    "PUBDATEALLSTYLE",
    "TITLESTYLE",
    "BOOKTITLESTYLE",
    "SERIESTITLESTYLE",
    "ALLTITLESTYLE",
    "JOURNALNAMESTYLE",
    "VOLUMESTYLE",
    "ISSUESTYLE",
    "PAGESSTYLE",
    "PUBLISHERSTYLE",
    "PUBPLACESTYLE",
    "REFNUMBERSTYLE",
    "AUTHORLISTABBREVIATEFIRSTSTYLE",
    "AUTHORLISTABBREVIATESUBSEQSTYLE",
    "EDITORLISTABBREVIATEFIRSTSTYLE",
    "EDITORLISTABBREVIATESUBSEQSTYLE",
    "SEDITORLISTABBREVIATEFIRSTSTYLE",
    "SEDITORLISTABBREVIATESUBSEQSTYLE",
    "ALLALISTABBREVIATEFIRSTSTYLE",
    "ALLALISTABBREVIATESUBSEQSTYLE",
    "AUTHORLISTAUTHORNAMESNAMEFIRSTINITIALSTYLE",
    "EDITORLISTAUTHORNAMESNAMEFIRSTINITIALSTYLE",
    "SEDITORLISTAUTHORNAMESNAMEFIRSTINITIALSTYLE",
    "ALLALISTAUTHORNAMESNAMEFIRSTINITIALSTYLE",
    "AUTHORLISTAUTHORNAMESNAMEOTHERINITIALSTYLE",
    "EDITORLISTAUTHORNAMESNAMEOTHERINITIALSTYLE",
    "SEDITORLISTAUTHORNAMESNAMEOTHERINITIALSTYLE",
    "ALLALISTAUTHORNAMESNAMEOTHERINITIALSTYLE",
    "SERIALSTYLE",
    "ADDRESSSTYLE",
    "USERDEF1STYLE",
    "USERDEF2STYLE",
    "USERDEF3STYLE",
    "USERDEF4STYLE",
    "USERDEF5STYLE",
    "ABSTRACTSTYLE",
    "TYPEOFWORKSTYLE",
    "AREASTYLE",
    "OSTYPESTYLE",
    "DEGREESTYLE",
    "RUNNINGTIMESTYLE",
    "CLASSCODEINTLSTYLE",
    "CLASSCODEUSSTYLE",
    "SENDEREMAILSTYLE",
    "RECIPIENTEMAILSTYLE",
    "MEDIATYPESTYLE",
    "NUMVOLUMESSTYLE",
    "EDITIONSTYLE",
    "COMPUTERSTYLE",
    "CONFERENCELOCATIONSTYLE",
    "REGISTRYNUMSTYLE",
    "CLASSIFICATIONSTYLE",
    "SECTIONSTYLE",
    "PAMPHLETNUMSTYLE",
    "CHAPTERNUMSTYLE",
    "NOTESSTYLE",
    "LINK0STYLE",
    "LINK1STYLE",
    "LINK2STYLE",
    "LINK3STYLE",
    "LINK4STYLE",
    "PAGESPAGERANGESTYLE",
    "CITEKEYSTYLE"};

  if ((sql_command = malloc(sql_command_len)) == NULL) {
    return 801;
  }

  drivername = dbi_driver_get_name(dbi_conn_get_driver(conn));

  /* read style elements from the citestyle table */
  if (!strcmp(my_dbi_conn_get_cap(conn, "multiple_db"), "t")) {
    sprintf(sql_command, "SELECT ID, STYLE, BIBLIOFIRSTINDENT, BIBLIOBLOCKINDENT, FONTSIZE FROM %s.CITSTYLE WHERE JOURNAL=%s", main_db, format_string);
  }
  else {
    sprintf(sql_command, "SELECT ID, STYLE, BIBLIOFIRSTINDENT, BIBLIOBLOCKINDENT, FONTSIZE FROM CITSTYLE WHERE JOURNAL=%s", format_string);
  }

  dbires_cit = dbi_conn_query(conn, sql_command);
  LOG_PRINT(LOG_DEBUG, sql_command);
  if (!dbires_cit || dbi_result_next_row(dbires_cit) == 0) {
    free(sql_command);
    LOG_PRINT(LOG_WARNING, get_status_msg(241));
    send_status(fd, 241, TERM_NO);
    return 241;
  }

  /* NULL check will be done later */
  bibliofirstindent = dbi_result_get_string(dbires_cit, "BIBLIOFIRSTINDENT");
  biblioblockindent = dbi_result_get_string(dbires_cit, "BIBLIOBLOCKINDENT");
  fontsize = dbi_result_get_string(dbires_cit, "FONTSIZE");

  /* now retrieve the styles for individual publication types */
  n_id = my_dbi_result_get_int_idval(dbires_cit, "ID");

  if (!strcmp(my_dbi_conn_get_cap(conn, "multiple_db"), "t")) {
    sprintf(sql_command, "SELECT PUBTYPE, QSTYLE, XSTYLE, YSTYLE, ZSTYLE, QALTERNATESTYLE, XALTERNATESTYLE, YALTERNATESTYLE, ZALTERNATESTYLE, PUBDATESTYLE, PUBDATESECSTYLE, PUBDATEALLSTYLE, TITLESTYLE, BOOKTITLESTYLE, SERIESTITLESTYLE, ALLTITLESTYLE, JOURNALNAMESTYLE, VOLUMESTYLE, ISSUESTYLE, PAGESSTYLE, PUBLISHERSTYLE, PUBPLACESTYLE, REFNUMBERSTYLE, QABBREVIATEFIRSTSTYLE, QABBREVIATESUBSEQSTYLE, XABBREVIATEFIRSTSTYLE, XABBREVIATESUBSEQSTYLE, YABBREVIATEFIRSTSTYLE, YABBREVIATESUBSEQSTYLE, ZABBREVIATEFIRSTSTYLE, ZABBREVIATESUBSEQSTYLE, QAUTHORNAMESNAMEFIRSTINITIALSTYLE, XAUTHORNAMESNAMEFIRSTINITIALSTYLE, YAUTHORNAMESNAMEFIRSTINITIALSTYLE, ZAUTHORNAMESNAMEFIRSTINITIALSTYLE, QAUTHORNAMESNAMEOTHERINITIALSTYLE, XAUTHORNAMESNAMEOTHERINITIALSTYLE, YAUTHORNAMESNAMEOTHERINITIALSTYLE, ZAUTHORNAMESNAMEOTHERINITIALSTYLE, SERIALSTYLE, ADDRESSSTYLE, USERDEF1STYLE, USERDEF2STYLE, USERDEF3STYLE, USERDEF4STYLE, USERDEF5STYLE, NOTESSTYLE, ABSTRACTSTYLE, TYPEOFWORKSTYLE, AREASTYLE, OSTYPESTYLE, DEGREESTYLE, RUNNINGTIMESTYLE, CLASSCODEINTLSTYLE, CLASSCODEUSSTYLE, SENDEREMAILSTYLE, RECIPIENTEMAILSTYLE, MEDIATYPESTYLE, NUMVOLUMESSTYLE, EDITIONSTYLE, COMPUTERSTYLE, CONFERENCELOCATIONSTYLE, REGISTRYNUMSTYLE, CLASSIFICATIONSTYLE, SECTIONSTYLE, PAMPHLETNUMSTYLE, CHAPTERNUMSTYLE, LINK0STYLE, LINK1STYLE, LINK2STYLE, LINK3STYLE, LINK4STYLE, PAGESPAGERANGESTYLE, CITEKEYSTYLE FROM %s.REFSTYLE WHERE %s.REFSTYLE.CITSTYLEID=%u", main_db, main_db, n_id);
  }
  else {
    sprintf(sql_command, "SELECT PUBTYPE, QSTYLE, XSTYLE, YSTYLE, ZSTYLE, QALTERNATESTYLE, XALTERNATESTYLE, YALTERNATESTYLE, ZALTERNATESTYLE, PUBDATESTYLE, PUBDATESECSTYLE, PUBDATEALLSTYLE, TITLESTYLE, BOOKTITLESTYLE, SERIESTITLESTYLE, ALLTITLESTYLE, JOURNALNAMESTYLE, VOLUMESTYLE, ISSUESTYLE, PAGESSTYLE, PUBLISHERSTYLE, PUBPLACESTYLE, REFNUMBERSTYLE, QABBREVIATEFIRSTSTYLE, QABBREVIATESUBSEQSTYLE, XABBREVIATEFIRSTSTYLE, XABBREVIATESUBSEQSTYLE, YABBREVIATEFIRSTSTYLE, YABBREVIATESUBSEQSTYLE, ZABBREVIATEFIRSTSTYLE, ZABBREVIATESUBSEQSTYLE, QAUTHORNAMESNAMEFIRSTINITIALSTYLE, XAUTHORNAMESNAMEFIRSTINITIALSTYLE, YAUTHORNAMESNAMEFIRSTINITIALSTYLE, ZAUTHORNAMESNAMEFIRSTINITIALSTYLE, QAUTHORNAMESNAMEOTHERINITIALSTYLE, XAUTHORNAMESNAMEOTHERINITIALSTYLE, YAUTHORNAMESNAMEOTHERINITIALSTYLE, ZAUTHORNAMESNAMEOTHERINITIALSTYLE, SERIALSTYLE, ADDRESSSTYLE, USERDEF1STYLE, USERDEF2STYLE, USERDEF3STYLE, USERDEF4STYLE, USERDEF5STYLE, NOTESSTYLE, ABSTRACTSTYLE, TYPEOFWORKSTYLE, AREASTYLE, OSTYPESTYLE, DEGREESTYLE, RUNNINGTIMESTYLE, CLASSCODEINTLSTYLE, CLASSCODEUSSTYLE, SENDEREMAILSTYLE, RECIPIENTEMAILSTYLE, MEDIATYPESTYLE, NUMVOLUMESSTYLE, EDITIONSTYLE, COMPUTERSTYLE, CONFERENCELOCATIONSTYLE, REGISTRYNUMSTYLE, CLASSIFICATIONSTYLE, SECTIONSTYLE, PAMPHLETNUMSTYLE, CHAPTERNUMSTYLE, LINK0STYLE, LINK1STYLE, LINK2STYLE, LINK3STYLE, LINK4STYLE, PAGESPAGERANGESTYLE, CITEKEYSTYLE FROM REFSTYLE WHERE REFSTYLE.CITSTYLEID=%u", n_id);
  }

  dbires_ref = dbi_conn_query(conn, sql_command);
  LOG_PRINT(LOG_DEBUG, sql_command);
  if (!dbires_ref) {
    dbi_result_free(dbires_cit);
    free(sql_command);
    LOG_PRINT(LOG_WARNING, get_status_msg(241));
    send_status(fd, 241, TERM_NO);
    return 241;
  }

  free(sql_command);

  /* output starts here */
  if (n_ref_format == REFDOCBK) {
    sprintf(outbuffer, "<!DOCTYPE style-sheet PUBLIC \"-//James Clark//DTD DSSSL Style Sheet//EN\" [\n<!ENTITY %s PUBLIC \"%s\" CDATA DSSSL>\n<!ENTITY %s PUBLIC \"%s\" CDATA DSSSL>\n]>\n<style-sheet>\n<style-specification id=\"%s\" use=\"%s\">\n<style-specification-body>\n", STYLESHEET_HTML_NAME, STYLESHEET_HTML_PUBID, STYLESHEET_PRINT_NAME, STYLESHEET_PRINT_PUBID, STYLESHEET_HTML_ID, STYLESHEET_HTML_BASENAME);
  }
  else {
    sprintf(outbuffer, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n<!-- REFDBSTYLESHEET --><xsl:import href=\"\"/><!-- /REFDBSTYLESHEET -->\n");
  }
  
  send_status(fd, 402, TERM_NO);
  numbyte = tiwrite(fd, outbuffer, TERM_NO);
  /*  	printf("outbuffer:%s<<\n", outbuffer); */
  if (numbyte == -1) {
    dbi_result_free(dbires_cit);
    dbi_result_free(dbires_ref);
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    return 110;
  }

  /* loop over all publication types */
  while (dbi_result_next_row(dbires_ref)) {
    item_pubtype = dbi_result_get_string_idx(dbires_ref, 1); /* 1-base index */
/*     LOG_PRINT(LOG_DEBUG, item_pubtype); */
    /* loop over all columns except pubtype which is a constant prefix */
    for (i = 1; i < dbi_result_get_numfields(dbires_ref); i++) {
      item = dbi_result_get_string_idx(dbires_ref, i+1); /* 1-base index */
      if (item && *item) {
/* 	LOG_PRINT(LOG_DEBUG, item); */
	if (!strcmp(tags[i-1], "AUTHORLISTALTERNATESTYLE")
	    ||!strcmp(tags[i-1], "EDITORLISTALTERNATESTYLE")
	    ||!strcmp(tags[i-1], "SEDITORLISTALTERNATESTYLE")
	    ||!strcmp(tags[i-1], "ALLALISTALTERNATESTYLE")) {
	  item++; /* skip leading "A" */
	}

	/* we assemble the variable name from the publication type and the column name. The variable value is the value of the column */
	if (n_ref_format == REFDOCBK) {
	  sprintf(outbuffer, "(define %s%s \"%s\")\n", item_pubtype, tags[i-1], item);
	}
	else {
	  sprintf(outbuffer, "<xsl:variable name=\"%s%s\">%s</xsl:variable>\n", item_pubtype, tags[i-1], item);
	}
	numbyte = tiwrite(fd, outbuffer, TERM_NO);
/*  	printf("outbuffer:%s<<\n", outbuffer); */
	if (numbyte == -1) {
	  dbi_result_free(dbires_cit);
	  dbi_result_free(dbires_ref);
	  LOG_PRINT(LOG_WARNING, get_status_msg(110));
	  return 110;
	}
      } /* end if */
    } /* end for */
  } /* end while */

  if (n_ref_format == REFDOCBK) {
    if (write_bibstyle_info(fd, bibliofirstindent, biblioblockindent, fontsize, 1 /* DSSSL */)) {
      dbi_result_free(dbires_cit);
      dbi_result_free(dbires_ref);
      return 110;
    }
    sprintf(outbuffer, "</style-specification-body>\n</style-specification>\n<style-specification id=\"%s\" use=\"%s\">\n<style-specification-body>\n", STYLESHEET_PRINT_ID, STYLESHEET_PRINT_BASENAME);
  }
  else {
    if (write_bibstyle_info(fd, bibliofirstindent, biblioblockindent, fontsize, 0 /* XSLT */)) {
      dbi_result_free(dbires_cit);
      dbi_result_free(dbires_ref);
      return 110;
    }
    strcpy(outbuffer, "</xsl:stylesheet>");
  }

  if (n_ref_format == REFDOCBK) {
    numbyte = tiwrite(fd, outbuffer, TERM_NO);
    /*  	printf("outbuffer:%s<<\n", outbuffer); */
    if (numbyte == -1) {
      dbi_result_free(dbires_cit);
      dbi_result_free(dbires_ref);
      LOG_PRINT(LOG_WARNING, get_status_msg(110));
      return 110;
    }
  }
  else {
    /* xsl stylesheet is done here */
    dbi_result_free(dbires_cit);
    dbi_result_free(dbires_ref);
    numbyte = tiwrite(fd, outbuffer, TERM_YES);
    /*  	printf("outbuffer:%s<<\n", outbuffer); */
    if (numbyte == -1) {
      LOG_PRINT(LOG_WARNING, get_status_msg(110));
      return 110;
    }
    return 0;
  }


  /* rewind. we can't jump to the first row and then use next_row as
     this would skip the first row. Instead we use next_row at the
     bottom of the while loop */
  haverow = dbi_result_first_row(dbires_ref);

  /* loop over all publication types */
  while (haverow) {
    item_pubtype = dbi_result_get_string_idx(dbires_ref, 1); /* 1-base index */

/*     LOG_PRINT(LOG_DEBUG, item_pubtype); */
    /* loop over all columns except pubtype which is a constant prefix*/
    for (i = 1; i < dbi_result_get_numfields(dbires_ref); i++) {
      item = dbi_result_get_string_idx(dbires_ref, i+1); /* 1-base index */
      if (item && *item) {
/* 	LOG_PRINT(LOG_DEBUG, item); */
	if (!strcmp(tags[i-1], "AUTHORLISTALTERNATESTYLE")
	    ||!strcmp(tags[i-1], "EDITORLISTALTERNATESTYLE")
	    ||!strcmp(tags[i-1], "SEDITORLISTALTERNATESTYLE")
	    ||!strcmp(tags[i-1], "ALLALISTALTERNATESTYLE")) {
	  item++; /* skip leading "A" */
	}
	/* we assemble the variable name from the publication type and the column name. The variable value is the value of the column */
	sprintf(outbuffer, "(define %s%s \"%s\")\n", item_pubtype, tags[i-1], item);
	numbyte = tiwrite(fd, outbuffer, TERM_NO);
/*  	printf("outbuffer:%s<<\n", outbuffer); */
	if (numbyte == -1) {
	  dbi_result_free(dbires_cit);
	  dbi_result_free(dbires_ref);
	  LOG_PRINT(LOG_WARNING, get_status_msg(110));
	  return 110;
	}
      } /* end if */
    } /* end for */
    haverow = dbi_result_next_row(dbires_ref);
  } /* end while */

  if (write_bibstyle_info(fd, bibliofirstindent, biblioblockindent, fontsize, 1 /* DSSSL */)) {
    dbi_result_free(dbires_cit);
    dbi_result_free(dbires_ref);
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    return 110;
  }

  dbi_result_free(dbires_cit);
  dbi_result_free(dbires_ref);

  sprintf(outbuffer, "</style-specification-body>\n</style-specification>\n<external-specification id=\"%s\" document=\"%s\">\n<external-specification id=\"%s\" document=\"%s\">\n</style-sheet>\n", STYLESHEET_HTML_BASENAME, STYLESHEET_HTML_NAME, STYLESHEET_PRINT_BASENAME, STYLESHEET_PRINT_NAME);
  numbyte = tiwrite(fd, outbuffer, TERM_YES);
  /*  	printf("outbuffer:%s<<\n", outbuffer); */
  if (numbyte == -1) {
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    return 110;
  }

/*    printf("done writing style spec\n"); */
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  write_bibstyle_info() writes bibliography style info as part of a
                        xslt/dsssl driver file

  static int write_bibstyle_info returns 1 in case of an error, 0 if successful

  int fd file descriptor to write data to

  const char* bibliofirstindent ptr to string containing bibliofirstindent info

  const char* biblioblockindent ptr to string containing biblioblockindent info

  const char* fontsize ptr to string containing fontsize info

  int type 0 = xslt output 1 = dsssl output

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int write_bibstyle_info(int fd, const char* bibliofirstindent, const char* biblioblockindent, const char* fontsize, int type) {
  char outbuffer[512];
  int numbyte;

  /* todo: check string lengths or use mstrcat et al. */

  *outbuffer = '\0';

  if (!type) { /* XSLT */
    strcpy(outbuffer, "<!--CSSFILE/--><!-- REFDBBIBSTYLE --><xsl:attribute-set name=\"refdb.bibliomset\" use-attribute-sets=\"normal.para.spacing\">");

    if (bibliofirstindent) {
      sprintf(outbuffer+strlen(outbuffer), "<xsl:attribute name=\"text-indent\">%s</xsl:attribute>", bibliofirstindent);
    }

    if (biblioblockindent) {
      sprintf(outbuffer+strlen(outbuffer), "<xsl:attribute name=\"start-indent\">%s</xsl:attribute>", biblioblockindent);
    }

    if (fontsize) {
      if (is_real_number(fontsize)) {
	sprintf(outbuffer+strlen(outbuffer), "<xsl:attribute name=\"font-size-adjust\">%s</xsl:attribute>", fontsize);
      }
      else {
	sprintf(outbuffer+strlen(outbuffer), "<xsl:attribute name=\"font-size\">%s</xsl:attribute>", fontsize);
      }
    }

    strcat(outbuffer, "</xsl:attribute-set><!-- /REFDBBIBSTYLE -->\n");
  }
  else { /* DSSSL */
    strcpy(outbuffer, "(define %stylesheet% #f)\n");

    if (bibliofirstindent) {
      sprintf(&(outbuffer[strlen(outbuffer)]), "(define refdb-text-indent %s)\n", bibliofirstindent);
    }
    else {
      strcat(outbuffer, "(define refdb-text-indent #f)\n");
    }

    if (biblioblockindent) {
      sprintf(&(outbuffer[strlen(outbuffer)]), "(define refdb-start-indent %s)\n", biblioblockindent);
    }
    else {
      strcat(outbuffer, "(define refdb-start-indent #f)\n");
    }

    if (fontsize) {
      sprintf(&(outbuffer[strlen(outbuffer)]), "(define refdb-font-size %s)\n", fontsize);
    }
    else {
      strcat(outbuffer, "(define refdb-font-size #f)\n");
    }
  }

  numbyte = tiwrite(fd, outbuffer, TERM_NO);
/*  	printf("outbuffer:%s<<\n", outbuffer); */
  if (numbyte == -1) {
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    return 1;
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  append_return_msg() assembles a message from a given static part
                        and a dbi error message

  static int append_return_msg returns 1 in case of an error, 0 if successful

  struct ADDRESULT *ptr_addresult ptr to a structure with add info

  int n_status number of status message

  const char* entry_id optional ptr to string denoting the reference that
                 caused the error
  dbi_conn conn ptr to an existing connection to the database
                        server

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int append_return_msg(struct ADDRESULT *ptr_addresult, int n_status, const char* entry_id, dbi_conn conn) {
  char* new_msg;
  char buffer[512]; /* error status + ref_id + database_name*/

  if (entry_id && *entry_id) {
    sprintf(buffer, "%s:%s\n", get_status_string(n_status), entry_id);
    if ((new_msg = mstrcat(ptr_addresult->msg, buffer, &(ptr_addresult->msg_len), 0)) == NULL) {
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      return 1;
    }
    else {
      ptr_addresult->msg = new_msg;
    }

    LOG_PRINT(LOG_WARNING, buffer);
  }
  else {
    if ((new_msg = mstrcat(ptr_addresult->msg, (char*)get_status_msg(n_status), &(ptr_addresult->msg_len), 0)) == NULL) {
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      return 1;
    }
    else {
      ptr_addresult->msg = new_msg;
    }

    LOG_PRINT(LOG_WARNING, get_status_msg(n_status));
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  indent_notbelow_dbib(): checks whether or not to indent below the current
                     element

  int indent_notbelow_dbib returns 1 if not to indent, 0 if to indent

  const char* name ptr to element name

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int indent_notbelow_dbib(const char* name)
{
  if (!strcmp(name, "bibliomset")) {
    return 1;
  }
  else {
    return 0;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  is_entry_dbib(): checks whether an element starts a new entry

  int is_entry_dbib returns 1 if entry starts, 0 if not

  const char* name ptr to element name

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int is_entry_dbib(const char* name) {
  if (!strcmp(name, "bibliomixed")) {
    return 1;
  }
  else {
    return 0;
  }
}
