/*++++++++++++++++++++++
  xmlout.c: refdb application server, xml export functions
  markus@mhoenicka.de 2002-09-05
  $Id: xmlout.c,v 1.6.2.8 2006/04/05 20:19:26 mhoenicka Exp $

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

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

#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <dbi/dbi.h>

#include "linklist.h"
#include "refdb.h"
#include "connect.h"
#include "refdbd.h"
#include "backend.h"
#include "xmlout.h"
#include "strfncs.h"
#include "dbfncs.h"
#include "outformats.h"

/* globals */
extern int n_log_level;

/* forward declarations of local functions */
static int iwrite_authorlist(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role);
static int iwrite_authornames(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role);
static int iwrite_authorseps(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role);
static int iwrite_authortext(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role);
static int iwrite_editortext(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role);
static int iwrite_simple(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* elname);
static int iwrite_twoatt(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* elname);
static int iwrite_journalname(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref);
static int iwrite_pages(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref);
static int iwrite_pubdate(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role);
static int iwrite_separator(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, unsigned int separator_id);
static int iwrite_title(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role);

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_elstart() writes a start tag of an element to a file descriptor

  int iwrite_elstart 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  const char* elname string containing the element name

  Liliform *ptr_first pointer to sentinel of a list of attributes

  int nis_empty 0 if element has data, 1 if element is empty
  
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int iwrite_elstart(struct CLIENT_REQUEST* ptr_clrequest, const char* elname, Liliform *ptr_first, int nis_empty) {
  Liliform *ptr_curr;
  char outbuffer[512];

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

  if (tiwrite(ptr_clrequest->fd, "<", TERM_NO) == -1) {
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    return 1;
  }

  if (tiwrite(ptr_clrequest->fd, elname, TERM_NO) == -1) {
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    return 1;
  }

  /* add attributes if available */
  if (ptr_first) {
    ptr_curr = ptr_first;
    while ((ptr_curr = get_next_liliform(ptr_curr)) != NULL) {
      if (ptr_curr->name && *(ptr_curr->name)
	  && ptr_curr->value && *(ptr_curr->value)) {
	sprintf(outbuffer, " %s=\"%s\"", ptr_curr->name, ptr_curr->value);
	if (tiwrite(ptr_clrequest->fd, outbuffer, TERM_NO) == -1) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(110));
	  return 1;
	}
      }
    }
  }

  if (nis_empty) {
    if (tiwrite(ptr_clrequest->fd, "/>\n", TERM_NO) == -1) {
      LOG_PRINT(LOG_WARNING, get_status_msg(110));
      return 1;
    }
  }
  else {
    if (tiwrite(ptr_clrequest->fd, ">", TERM_NO) == -1) {
      LOG_PRINT(LOG_WARNING, get_status_msg(110));
      return 1;
    }
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_elend() writes an end tag of an element to a file descriptor

  int iwrite_elstart 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  const char* elname string containing the element name

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int iwrite_elend(struct CLIENT_REQUEST* ptr_clrequest, const char* elname) {
  if (!elname || !*elname) {
    /* nothing to do */
    return 0;
  }

  if (tiwrite(ptr_clrequest->fd, "</", TERM_NO) == -1) {
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    return 1;
  }

  if (tiwrite(ptr_clrequest->fd, elname, TERM_NO) == -1) {
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    return 1;
  }

  if (tiwrite(ptr_clrequest->fd, ">\n", TERM_NO) == -1) {
    LOG_PRINT(LOG_WARNING, get_status_msg(110));
    return 1;
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_element() writes an element to a file descriptor

  int iwrite_element 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  const char* elname string containing the element name

  Liliform *ptr_first pointer to sentinel of a list of attributes
  
  const char *elvalue ptr to string with element value

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int iwrite_element(struct CLIENT_REQUEST* ptr_clrequest, const char* elname, Liliform *ptr_first, const char *elvalue) {
  char* entitize_buf;

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

  if (iwrite_elstart(ptr_clrequest, elname, ptr_first, 0)) {
    return 1;
  }

  /* take care of entities */
  if ((entitize_buf = strdup(elvalue)) == NULL
      || sgml_entitize(&entitize_buf, RISX) == NULL) {
      return 1;
  }

  if (tiwrite(ptr_clrequest->fd, entitize_buf, TERM_NO) == -1) {
    free(entitize_buf);
    return 1;
  }

  free(entitize_buf);

  if (iwrite_elend(ptr_clrequest, elname)) {
    return 1;
  }
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_pubtype() writes a pubtype element to a file descriptor. This
                   works also for INTEXTDEF, AUTHORONLY etc.

  int iwrite_pubtype 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  const char* type string containing the pubtype name

  unsigned int citstyle_id id of the current citstyle entry

  dbi_conn conn connection to database server

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int iwrite_pubtype(struct CLIENT_REQUEST* ptr_clrequest, const char* type, unsigned int citstyle_id, dbi_conn conn) {
  char sql_command[512];
  const char *item;
  const char *my_type;
  const char type_intext[] = "INTEXT";
  unsigned int refstyle_id;

  dbi_result dbires_ref;
  dbi_result dbires_pos;
  Liliform sentinel;

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

  /* start pubtype element */
  my_type = type;

  if (!strcmp(type, "INTEXTDEF")) {
    iwrite_elstart(ptr_clrequest, type, NULL, 0);
    my_type = type_intext;
  }
  else if (!strcmp(type, "AUTHORONLY")
      || !strcmp(type, "YEARONLY")) {
    iwrite_elstart(ptr_clrequest, type, NULL, 0);
  }
  else {
    if (insert_liliform(&sentinel, "TYPE", (char*)type)) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return 1;
    }
    iwrite_elstart(ptr_clrequest, "PUBTYPE", &sentinel, 0);
    delete_all_liliform(&sentinel);
  }

  sprintf(sql_command, "SELECT * FROM REFSTYLE WHERE PUBTYPE=\'%s\' AND CITSTYLEID=%u", my_type, 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)) { /* should run only once */
      refstyle_id = my_dbi_result_get_int_idval(dbires_ref, "ID");
      sprintf(sql_command, "SELECT * FROM POSITIONS WHERE REFSTYLEID=%u ORDER BY POS", refstyle_id);
      dbires_pos = dbi_conn_query(conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (dbires_pos) {
	/* loop over all positions */
	while (dbi_result_next_row(dbires_pos)) {
	  item = my_dbi_result_get_string(dbires_pos, "TYPE");

	  /* first the simple ones */
	  if (!strcmp(item, "ISSUE")
	      || !strcmp(item, "PUBLISHER")
	      || !strcmp(item, "PUBPLACE")
	      || !strcmp(item, "SERIAL")
	      || !strcmp(item, "VOLUME")
	      || !strcmp(item, "ADDRESS")
	      || !strcmp(item, "NOTES")
	      || !strcmp(item, "ABSTRACT")
	      || !strcmp(item, "TYPEOFWORK")
	      || !strcmp(item, "AREA")
	      || !strcmp(item, "OSTYPE")
	      || !strcmp(item, "DEGREE")
	      || !strcmp(item, "RUNNINGTIME")
	      || !strcmp(item, "CLASSCODEINTL")
	      || !strcmp(item, "CLASSCODEUS")
	      || !strcmp(item, "SENDEREMAIL")
	      || !strcmp(item, "RECIPIENTEMAIL")
	      || !strcmp(item, "MEDIATYPE")
	      || !strcmp(item, "NUMVOLUMES")
	      || !strcmp(item, "EDITION")
	      || !strcmp(item, "COMPUTER")
	      || !strcmp(item, "CONFERENCELOCATION")
	      || !strcmp(item, "REGISTRYNUM")
	      || !strcmp(item, "CLASSIFICATION")
	      || !strcmp(item, "SECTION")
	      || !strcmp(item, "PAMPHLETNUM")
	      || !strcmp(item, "CHAPTERNUM")
	      || !strcmp(item, "CITEKEY")
	      || !strcmp(item, "REFNUMBER")) {
	    iwrite_simple(ptr_clrequest, dbires_ref, item);
	  }
	  /* now those with attributes */
	  else if (!strcmp(item, "AUTHORLIST")) {
	    iwrite_authorlist(ptr_clrequest, dbires_ref, "PART");
	  }
	  else if (!strcmp(item, "EDITORLIST")) {
	    iwrite_authorlist(ptr_clrequest, dbires_ref, "PUB");
	  }
	  else if (!strcmp(item, "SEDITORLIST")) {
	    iwrite_authorlist(ptr_clrequest, dbires_ref, "SERIES");
	  }
	  else if (!strcmp(item, "ALLALIST")) {
	    iwrite_authorlist(ptr_clrequest, dbires_ref, "ALL");
	  }
	  else if (!strcmp(item, "JOURNALNAME")) {
	    iwrite_journalname(ptr_clrequest, dbires_ref);
	  }
	  else if (!strcmp(item, "PAGES")) {
	    iwrite_pages(ptr_clrequest, dbires_ref);
	  }
	  else if (!strcmp(item, "PUBDATE")) {
	    iwrite_pubdate(ptr_clrequest, dbires_ref, "PRIMARY");
	  }
	  else if (!strcmp(item, "PUBDATESEC")) {
	    iwrite_pubdate(ptr_clrequest, dbires_ref, "SECONDARY");
	  }
	  else if (!strcmp(item, "PUBDATEALL")) {
	    iwrite_pubdate(ptr_clrequest, dbires_ref, "ALL");
	  }
	  else if (!strcmp(item, "USERDEF1")
		   || !strcmp(item, "USERDEF2")
		   || !strcmp(item, "USERDEF3")
		   || !strcmp(item, "USERDEF4")
		   || !strcmp(item, "USERDEF5")
		   || !strcmp(item, "LINK0")
		   || !strcmp(item, "LINK1")
		   || !strcmp(item, "LINK2")
		   || !strcmp(item, "LINK3")
		   || !strcmp(item, "LINK4")) {
	    iwrite_twoatt(ptr_clrequest, dbires_ref, item);
	  }
	  else if (!strcmp(item, "SEPARATOR")) {
	    iwrite_separator(ptr_clrequest, dbires_ref, my_dbi_result_get_int_idval(dbires_pos, "SEPARATORID"));
	  }
	  else if (!strcmp(item, "TITLE")) {
	    iwrite_title(ptr_clrequest, dbires_ref, "PART");
	  }
	  else if (!strcmp(item, "BOOKTITLE")) {
	    iwrite_title(ptr_clrequest, dbires_ref, "PUB");
	  }
	  else if (!strcmp(item, "SERIESTITLE")) {
	    iwrite_title(ptr_clrequest, dbires_ref, "SERIES");
	  }
	  else if (!strcmp(item, "ALLTITLE")) {
	    iwrite_title(ptr_clrequest, dbires_ref, "ALL");
	  }
	  /* ToDo: else: something is wrong here */
	}
      }
    }
  }

  /* end pubtype element */
 if (!strcmp(type, "AUTHORONLY")
     || !strcmp(type, "YEARONLY")
     || !strcmp(type, "INTEXTDEF")) {
    iwrite_elend(ptr_clrequest, type);
  }
  else {
    iwrite_elend(ptr_clrequest, "PUBTYPE");
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_months() writes a months element to a file descriptor.

  int iwrite_months 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires ptr to citstyle query result structure

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int iwrite_months(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires) {
  int i;
  int j;
  char monthnames[4][12][12] = {
    {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"},
    {"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"}
  };
  Liliform sentinel;

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

  /* start months block */
  iwrite_elstart(ptr_clrequest, "MONTHS", NULL, 0);

  /* loop over all months */
  for (i = 0; i < 12; i++) {
    /* fill linked list with attributes abbrev, full, threelet */
    for (j = 1; j < 4; j++) {
      if (insert_liliform(&sentinel, monthnames[j][i]+3, (char*)my_dbi_result_get_string(dbires, monthnames[j][i]))) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	delete_all_liliform(&sentinel);
	return 1;
      }
    }
    iwrite_elstart(ptr_clrequest, monthnames[0][i], &sentinel, 1);
    delete_all_liliform(&sentinel);
  }

  iwrite_elend(ptr_clrequest, "MONTHS");

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_authorlist() writes an authorlist element to a file descriptor.

  static int iwrite_authorlist 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  const char* role ptr to string with the authorlist role

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_authorlist(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role) {
  char *abbreviatefirst;
  char *abbreviatefirstmaxauthor;
  char *abbreviatefirstdisplayauthor;
  char *abbreviatefirststyle;
  char *abbreviatesubseq;
  char *abbreviatesubseqmaxauthor;
  char *abbreviatesubseqdisplayauthor;
  char *abbreviatesubseqstyle;

  char authorfields[4][15][31] = {
    {"QSTYLE", "QALTERNATESTYLE", "QALTERNATETEXT", "QABBREVIATEFIRST", "QABBREVIATEFIRSTMAXAUTHOR", "QABBREVIATEFIRSTDISPLAYAUTHOR", "QABBREVIATEFIRSTSTYLE", "QABBREVIATESUBSEQ", "QABBREVIATESUBSEQMAXAUTHOR", "QABBREVIATESUBSEQDISPLAYAUTHOR", "QABBREVIATESUBSEQSTYLE", "QAEMPTY", "QASAME", "QPRECEEDING", "QFOLLOWING"},
    {"XSTYLE", "XALTERNATESTYLE", "XALTERNATETEXT", "XABBREVIATEFIRST", "XABBREVIATEFIRSTMAXAUTHOR", "XABBREVIATEFIRSTDISPLAYAUTHOR", "XABBREVIATEFIRSTSTYLE", "XABBREVIATESUBSEQ", "XABBREVIATESUBSEQMAXAUTHOR", "XABBREVIATESUBSEQDISPLAYAUTHOR", "XABBREVIATESUBSEQSTYLE", "XAEMPTY", "XASAME", "XPRECEEDING", "XFOLLOWING"},
    {"YSTYLE", "YALTERNATESTYLE", "YALTERNATETEXT", "YABBREVIATEFIRST", "YABBREVIATEFIRSTMAXAUTHOR", "YABBREVIATEFIRSTDISPLAYAUTHOR", "YABBREVIATEFIRSTSTYLE", "YABBREVIATESUBSEQ", "YABBREVIATESUBSEQMAXAUTHOR", "YABBREVIATESUBSEQDISPLAYAUTHOR", "YABBREVIATESUBSEQSTYLE", "YAEMPTY", "YASAME", "YPRECEEDING", "YFOLLOWING"},
    {"ZSTYLE", "ZALTERNATESTYLE", "ZALTERNATETEXT", "ZABBREVIATEFIRST", "ZABBREVIATEFIRSTMAXAUTHOR", "ZABBREVIATEFIRSTDISPLAYAUTHOR", "ZABBREVIATEFIRSTSTYLE", "ZABBREVIATESUBSEQ", "ZABBREVIATESUBSEQMAXAUTHOR", "ZABBREVIATESUBSEQDISPLAYAUTHOR", "ZABBREVIATESUBSEQSTYLE", "ZAEMPTY", "ZASAME", "ZPRECEEDING", "ZFOLLOWING"}
  };

  int n_role = 0;
  Liliform sentinel;

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

  if (!strcmp(role, "PART")) {
    n_role = 0;
  }
  else if (!strcmp(role, "PUB")) {
    n_role = 1;
  }
  else if (!strcmp(role, "SERIES")) {
    n_role = 2;
  }
  else if (!strcmp(role, "ALL")) {
    n_role = 3;
  }

  /* get required strings */
  abbreviatefirst = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][3]);
  abbreviatefirstmaxauthor = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][4]);
  abbreviatefirstdisplayauthor = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][5]);
  abbreviatefirststyle = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][6]);
  abbreviatesubseq = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][7]);
  abbreviatesubseqmaxauthor = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][8]);
  abbreviatesubseqdisplayauthor = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][9]);
  abbreviatesubseqstyle = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][10]);

  /* begin authorlist block */
  if (insert_liliform(&sentinel, "STYLE", (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][0]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "ALTERNATESTYLE", (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][1]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "ALTERNATETEXT", (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][2]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "ROLE", (char*)role)) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  iwrite_elstart(ptr_clrequest, "AUTHORLIST", &sentinel, 0);

  delete_all_liliform(&sentinel);

  /* preceeding */
  iwrite_element(ptr_clrequest, "PRECEEDING", NULL, my_dbi_result_get_string(dbires_ref, authorfields[n_role][13]));

  /* following */
  iwrite_element(ptr_clrequest, "FOLLOWING", NULL, my_dbi_result_get_string(dbires_ref, authorfields[n_role][14]));

  if ((abbreviatefirst && *abbreviatefirst)
      || (abbreviatefirstmaxauthor && *abbreviatefirstmaxauthor)
      || (abbreviatefirstdisplayauthor && *abbreviatefirstdisplayauthor)
      || (abbreviatefirststyle && *abbreviatefirststyle)) {
    /* start abbreviatefirst block */
    if (insert_liliform(&sentinel, "MAXAUTHOR", abbreviatefirstmaxauthor)) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return 1;
    }

    if (insert_liliform(&sentinel, "DISPLAYAUTHOR", abbreviatefirstdisplayauthor)) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return 1;
    }

    if (insert_liliform(&sentinel, "STYLE", abbreviatefirststyle)) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return 1;
    }

    iwrite_elstart(ptr_clrequest, "ABBREVIATEFIRST", &sentinel, 0);

    delete_all_liliform(&sentinel);

    tiwrite(ptr_clrequest->fd, abbreviatefirst, TERM_NO);

    /* end abbreviatefirst block */
    iwrite_elend(ptr_clrequest, "ABBREVIATEFIRST");
  }

  if ((abbreviatesubseq && *abbreviatesubseq)
      || (abbreviatesubseqmaxauthor && *abbreviatesubseqmaxauthor)
      || (abbreviatesubseqdisplayauthor && *abbreviatesubseqdisplayauthor)
      || (abbreviatesubseqstyle && *abbreviatesubseqstyle)) {
    /* start abbreviatesubseq block */
    if (insert_liliform(&sentinel, "MAXAUTHOR", abbreviatesubseqmaxauthor)) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return 1;
    }

    if (insert_liliform(&sentinel, "DISPLAYAUTHOR", abbreviatesubseqdisplayauthor)) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return 1;
    }

    if (insert_liliform(&sentinel, "STYLE", abbreviatesubseqstyle)) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return 1;
    }

    iwrite_elstart(ptr_clrequest, "ABBREVIATESUBSEQ", &sentinel, 0);

    delete_all_liliform(&sentinel);

    tiwrite(ptr_clrequest->fd, abbreviatesubseq, TERM_NO);

    /* end abbreviatesubseq block */
    iwrite_elend(ptr_clrequest, "ABBREVIATESUBSEQ");
  }

  /* aempty */
  iwrite_element(ptr_clrequest, "AEMPTY", NULL, my_dbi_result_get_string(dbires_ref, authorfields[n_role][11]));

  /* asame */
  iwrite_element(ptr_clrequest, "ASAME", NULL, my_dbi_result_get_string(dbires_ref, authorfields[n_role][12]));

  /* authornames, authorseps, text elements */
  iwrite_authorseps(ptr_clrequest, dbires_ref, role);
  iwrite_authornames(ptr_clrequest, dbires_ref, role);
  iwrite_authortext(ptr_clrequest, dbires_ref, role);
  iwrite_editortext(ptr_clrequest, dbires_ref, role);

  /* end authorlist block */
  iwrite_elend(ptr_clrequest, "AUTHORLIST");

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_authornames() writes an authornames element to a file descriptor.

  static int iwrite_authornames 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  const char* role ptr to string with the authorlist role

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_authornames(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role) {
  char *namefirstnameorder;
  char *namefirstinitialstyle;
  char *namefirstuppercase;
  char *nameothernameorder;
  char *nameotherinitialstyle;
  char *nameotheruppercase;

  char authorfields[4][6][34] = {
    {"QAUTHORNAMESNAMEFIRSTNAMEORDER", "QAUTHORNAMESNAMEFIRSTINITIALSTYLE", "QAUTHORNAMESNAMEFIRSTUPPERCASE", "QAUTHORNAMESNAMEOTHERNAMEORDER", "QAUTHORNAMESNAMEOTHERINITIALSTYLE", "QAUTHORNAMESNAMEOTHERUPPERCASE"},
    {"XAUTHORNAMESNAMEFIRSTNAMEORDER", "XAUTHORNAMESNAMEFIRSTINITIALSTYLE", "XAUTHORNAMESNAMEFIRSTUPPERCASE", "XAUTHORNAMESNAMEOTHERNAMEORDER", "XAUTHORNAMESNAMEOTHERINITIALSTYLE", "XAUTHORNAMESNAMEOTHERUPPERCASE"},
    {"YAUTHORNAMESNAMEFIRSTNAMEORDER", "YAUTHORNAMESNAMEFIRSTINITIALSTYLE", "YAUTHORNAMESNAMEFIRSTUPPERCASE", "YAUTHORNAMESNAMEOTHERNAMEORDER", "YAUTHORNAMESNAMEOTHERINITIALSTYLE", "YAUTHORNAMESNAMEOTHERUPPERCASE"},
    {"ZAUTHORNAMESNAMEFIRSTNAMEORDER", "ZAUTHORNAMESNAMEFIRSTINITIALSTYLE", "ZAUTHORNAMESNAMEFIRSTUPPERCASE", "ZAUTHORNAMESNAMEOTHERNAMEORDER", "ZAUTHORNAMESNAMEOTHERINITIALSTYLE", "ZAUTHORNAMESNAMEOTHERUPPERCASE"}
  };

  int n_role = 0;
  Liliform sentinel;

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

  if (!strcmp(role, "PART")) {
    n_role = 0;
  }
  else if (!strcmp(role, "PUB")) {
    n_role = 1;
  }
  else if (!strcmp(role, "SERIES")) {
    n_role = 2;
  }
  else if (!strcmp(role, "ALL")) {
    n_role = 3;
  }

  /* get required strings */
  namefirstnameorder = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][0]);
  namefirstinitialstyle = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][1]);
  namefirstuppercase = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][2]);
  nameothernameorder = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][3]);
  nameotherinitialstyle = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][4]);
  nameotheruppercase = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][5]);

  if ((namefirstnameorder && *namefirstnameorder)
      || (namefirstinitialstyle && *namefirstinitialstyle)
      || (namefirstuppercase && *namefirstuppercase)
      || (nameothernameorder && *nameothernameorder)
      || (nameotherinitialstyle && *nameotherinitialstyle)
      || (nameotheruppercase && *nameotheruppercase)) {
    /* begin authornames block */
    iwrite_elstart(ptr_clrequest, "AUTHORNAMES", &sentinel, 0);

    if ((namefirstnameorder && *namefirstnameorder)
	|| (namefirstinitialstyle && *namefirstinitialstyle)
	|| (namefirstuppercase && *namefirstuppercase)) {
      /* start namefirst block */
      if (insert_liliform(&sentinel, "NAMEORDER", namefirstnameorder)) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return 1;
      }

      if (insert_liliform(&sentinel, "INITIALSTYLE", namefirstinitialstyle)) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return 1;
      }

      if (insert_liliform(&sentinel, "UPPERCASE", namefirstuppercase)) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return 1;
      }

      iwrite_elstart(ptr_clrequest, "NAMEFIRST", &sentinel, 1);

      delete_all_liliform(&sentinel);
    }

    if ((nameothernameorder && *nameothernameorder)
	|| (nameotherinitialstyle && *nameotherinitialstyle)
	|| (nameotheruppercase && *nameotheruppercase)) {
      /* start nameother block */
      if (insert_liliform(&sentinel, "NAMEORDER", nameothernameorder)) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return 1;
      }

      if (insert_liliform(&sentinel, "INITIALSTYLE", nameotherinitialstyle)) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return 1;
      }

      if (insert_liliform(&sentinel, "UPPERCASE", nameotheruppercase)) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return 1;
      }

      iwrite_elstart(ptr_clrequest, "NAMEOTHER", &sentinel, 1);

      delete_all_liliform(&sentinel);
    }

    /* end authorlist block */
    iwrite_elend(ptr_clrequest, "AUTHORNAMES");
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_authorseps() writes an authorseps element to a file descriptor.

  static int iwrite_authorseps 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  const char* role ptr to string with the authorlist role

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_authorseps(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role) {
  char *twoseps;
  char *threesepseach;
  char *threesepslast;

  char authorfields[4][3][34] = {
    {"QAUTHORSEPSTWOSEPS", "QAUTHORSEPSTHREESEPSTHREESEPSEACH", "QAUTHORSEPSTHREESEPSTHREESEPSLAST"},
    {"XAUTHORSEPSTWOSEPS", "XAUTHORSEPSTHREESEPSTHREESEPSEACH", "XAUTHORSEPSTHREESEPSTHREESEPSLAST"},
    {"YAUTHORSEPSTWOSEPS", "YAUTHORSEPSTHREESEPSTHREESEPSEACH", "YAUTHORSEPSTHREESEPSTHREESEPSLAST"},
    {"ZAUTHORSEPSTWOSEPS", "ZAUTHORSEPSTHREESEPSTHREESEPSEACH", "ZAUTHORSEPSTHREESEPSTHREESEPSLAST"}
  };

  int n_role = 0;
  Liliform sentinel;

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

  if (!strcmp(role, "PART")) {
    n_role = 0;
  }
  else if (!strcmp(role, "PUB")) {
    n_role = 1;
  }
  else if (!strcmp(role, "SERIES")) {
    n_role = 2;
  }
  else if (!strcmp(role, "ALL")) {
    n_role = 3;
  }

  /* get required strings */
  twoseps = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][0]);
  threesepseach = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][1]);
  threesepslast = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][2]);

  if ((twoseps && *twoseps)
      || (threesepseach && *threesepseach)
      || (threesepslast && *threesepslast)) {
    /* begin authorseps block */
    iwrite_elstart(ptr_clrequest, "AUTHORSEPS", &sentinel, 0);

    iwrite_element(ptr_clrequest, "TWOSEPS", NULL, twoseps);

    if ((threesepseach && *threesepseach)
      || (threesepslast && *threesepslast)) {
      /* start threeseps block */
      iwrite_elstart(ptr_clrequest, "THREESEPS", &sentinel, 0);

      iwrite_element(ptr_clrequest, "THREESEPSEACH", NULL, threesepseach);
      iwrite_element(ptr_clrequest, "THREESEPSLAST", NULL, threesepslast);
      iwrite_elend(ptr_clrequest, "THREESEPS");
    }

    /* end authorseps block */
    iwrite_elend(ptr_clrequest, "AUTHORSEPS");
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_authortext() writes an authortext element to a file descriptor.

  static int iwrite_authortext 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  const char* role ptr to string with the authorlist role

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_authortext(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role) {
  char *singlepreceeding;
  char *singlefollowing;
  char *multiplepreceeding;
  char *multiplefollowing;

  char authorfields[4][4][34] = {
    {"QTEXTTEXTSINGLEPRECEEDING", "QTEXTTEXTSINGLEFOLLOWING", "QTEXTTEXTMULTIPLEPRECEEDING", "QTEXTTEXTMULTIPLEFOLLOWING"},
    {"XTEXTTEXTSINGLEPRECEEDING", "XTEXTTEXTSINGLEFOLLOWING", "XTEXTTEXTMULTIPLEPRECEEDING", "XTEXTTEXTMULTIPLEFOLLOWING"},
    {"YTEXTTEXTSINGLEPRECEEDING", "YTEXTTEXTSINGLEFOLLOWING", "YTEXTTEXTMULTIPLEPRECEEDING", "YTEXTTEXTMULTIPLEFOLLOWING"},
    {"ZTEXTTEXTSINGLEPRECEEDING", "ZTEXTTEXTSINGLEFOLLOWING", "ZTEXTTEXTMULTIPLEPRECEEDING", "ZTEXTTEXTMULTIPLEFOLLOWING"}
  };

  int n_role = 0;
  Liliform sentinel;

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

  if (!strcmp(role, "PART")) {
    n_role = 0;
  }
  else if (!strcmp(role, "PUB")) {
    n_role = 1;
  }
  else if (!strcmp(role, "SERIES")) {
    n_role = 2;
  }
  else if (!strcmp(role, "ALL")) {
    n_role = 3;
  }

  /* get required strings */
  singlepreceeding = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][0]);
  singlefollowing = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][1]);
  multiplepreceeding = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][2]);
  multiplefollowing = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][3]);

  if ((singlepreceeding && *singlepreceeding)
      || (singlefollowing && *singlefollowing)
      || (multiplepreceeding && *multiplepreceeding)
      || (multiplefollowing && *multiplefollowing)) {
    /* begin text block */
    iwrite_elstart(ptr_clrequest, "TEXT", &sentinel, 0);

    if ((singlepreceeding && *singlepreceeding)
      || (singlefollowing && *singlefollowing)) {
      /* start textsingle block */
      iwrite_elstart(ptr_clrequest, "TEXTSINGLE", &sentinel, 0);

      iwrite_element(ptr_clrequest, "PRECEEDING", NULL, singlepreceeding);
      iwrite_element(ptr_clrequest, "FOLLOWING", NULL, singlefollowing);
      iwrite_elend(ptr_clrequest, "TEXTSINGLE");
    }

    if ((multiplepreceeding && *multiplepreceeding)
      || (multiplefollowing && *multiplefollowing)) {
      /* start textmultiple block */
      iwrite_elstart(ptr_clrequest, "TEXTMULTIPLE", &sentinel, 0);

      iwrite_element(ptr_clrequest, "PRECEEDING", NULL, multiplepreceeding);
      iwrite_element(ptr_clrequest, "FOLLOWING", NULL, multiplefollowing);
      iwrite_elend(ptr_clrequest, "TEXTMULTIPLE");
    }

    /* end authorseps block */
    iwrite_elend(ptr_clrequest, "TEXT");
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_editortext() writes an authortext element (for editors) to a
                      file descriptor.

  static int iwrite_editortext 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  const char* role ptr to string with the authorlist role

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_editortext(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role) {
  char *singlepreceeding;
  char *singlefollowing;
  char *multiplepreceeding;
  char *multiplefollowing;

  char authorfields[4][4][34] = {
    {"QTEXTEDTEXTSINGLEPRECEEDING", "QTEXTEDTEXTSINGLEFOLLOWING", "QTEXTEDTEXTMULTIPLEPRECEEDING", "QTEXTEDTEXTMULTIPLEFOLLOWING"},
    {"XTEXTEDTEXTSINGLEPRECEEDING", "XTEXTEDTEXTSINGLEFOLLOWING", "XTEXTEDTEXTMULTIPLEPRECEEDING", "XTEXTEDTEXTMULTIPLEFOLLOWING"},
    {"YTEXTEDTEXTSINGLEPRECEEDING", "YTEXTEDTEXTSINGLEFOLLOWING", "YTEXTEDTEXTMULTIPLEPRECEEDING", "YTEXTEDTEXTMULTIPLEFOLLOWING"},
    {"ZTEXTEDTEXTSINGLEPRECEEDING", "ZTEXTEDTEXTSINGLEFOLLOWING", "ZTEXTEDTEXTMULTIPLEPRECEEDING", "ZTEXTEDTEXTMULTIPLEFOLLOWING"}
  };

  int n_role = 0;
  Liliform sentinel;

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

  if (!strcmp(role, "PART")) {
    n_role = 0;
  }
  else if (!strcmp(role, "PUB")) {
    n_role = 1;
  }
  else if (!strcmp(role, "SERIES")) {
    n_role = 2;
  }
  else if (!strcmp(role, "ALL")) {
    n_role = 3;
  }

  /* get required strings */
  singlepreceeding = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][0]);
  singlefollowing = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][1]);
  multiplepreceeding = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][2]);
  multiplefollowing = (char*)my_dbi_result_get_string(dbires_ref, authorfields[n_role][3]);

  if ((singlepreceeding && *singlepreceeding)
      || (singlefollowing && *singlefollowing)
      || (multiplepreceeding && *multiplepreceeding)
      || (multiplefollowing && *multiplefollowing)) {
    /* begin text block */
    iwrite_elstart(ptr_clrequest, "TEXTED", &sentinel, 0);

    if ((singlepreceeding && *singlepreceeding)
      || (singlefollowing && *singlefollowing)) {
      /* start textsingle block */
      iwrite_elstart(ptr_clrequest, "TEXTSINGLE", &sentinel, 0);

      iwrite_element(ptr_clrequest, "PRECEEDING", NULL, singlepreceeding);
      iwrite_element(ptr_clrequest, "FOLLOWING", NULL, singlefollowing);
      iwrite_elend(ptr_clrequest, "TEXTSINGLE");
    }

    if ((multiplepreceeding && *multiplepreceeding)
      || (multiplefollowing && *multiplefollowing)) {
      /* start textmultiple block */
      iwrite_elstart(ptr_clrequest, "TEXTMULTIPLE", &sentinel, 0);

      iwrite_element(ptr_clrequest, "PRECEEDING", NULL, multiplepreceeding);
      iwrite_element(ptr_clrequest, "FOLLOWING", NULL, multiplefollowing);
      iwrite_elend(ptr_clrequest, "TEXTMULTIPLE");
    }

    /* end authorseps block */
    iwrite_elend(ptr_clrequest, "TEXTED");
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_journalname() writes a journalname element to a file descriptor.

  static int iwrite_journalname 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_journalname(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref) {
  Liliform sentinel;

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

  if (insert_liliform(&sentinel, "STYLE", (char*)my_dbi_result_get_string(dbires_ref, "JOURNALNAMESTYLE"))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "PUNCTUATION", (char*)my_dbi_result_get_string(dbires_ref, "JOURNALNAMEPUNCTUATION"))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "DEFAULTTEXT", (char*)my_dbi_result_get_string(dbires_ref, "JOURNALNAMEDEFAULTTEXT"))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "CASE", (char*)my_dbi_result_get_string(dbires_ref, "JOURNALNAMECASE"))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "ALTERNATETEXT", (char*)my_dbi_result_get_string(dbires_ref, "JOURNALNAMEALTERNATETEXT"))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  iwrite_elstart(ptr_clrequest, "JOURNALNAME", &sentinel, 0);

  delete_all_liliform(&sentinel);

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

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

  iwrite_elend(ptr_clrequest, "JOURNALNAME");

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_pages() writes a pages element to a file descriptor.

  static int iwrite_pages 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_pages(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref) {
  char *pagespagerangetype;
  char *pagespagerangepreceeding;
  char *pagespagerangefollowing;
  char *pagespagerangerangeseparator;
  char *pagessinglepagepreceeding;
  char *pagessinglepagefollowing;
  Liliform sentinel;

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

  /* get required values */
  pagespagerangetype = (char*)my_dbi_result_get_string(dbires_ref, "PAGESPAGERANGETYPE");
  pagespagerangepreceeding =(char*) my_dbi_result_get_string(dbires_ref, "PAGESPAGERANGEPRECEEDING");
  pagespagerangefollowing = (char*)my_dbi_result_get_string(dbires_ref, "PAGESPAGERANGEFOLLOWING");
  pagespagerangerangeseparator = (char*)my_dbi_result_get_string(dbires_ref, "PAGESPAGERANGERANGESEPARATOR");
  pagessinglepagepreceeding = (char*)my_dbi_result_get_string(dbires_ref, "PAGESSINGLEPAGEPRECEEDING");
  pagessinglepagefollowing = (char*)my_dbi_result_get_string(dbires_ref, "PAGESSINGLEPAGEFOLLOWING");

  if (insert_liliform(&sentinel, "STYLE", (char*)my_dbi_result_get_string(dbires_ref, "PAGESSTYLE"))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  iwrite_elstart(ptr_clrequest, "PAGES", &sentinel, 0);

  delete_all_liliform(&sentinel);

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

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

  if ((pagessinglepagepreceeding && *pagessinglepagepreceeding)
      || (pagessinglepagefollowing && *pagessinglepagefollowing)) {
    /* start singlepage block */
    iwrite_elstart(ptr_clrequest, "SINGLEPAGE", NULL, 0);

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

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

    /* end singlepage block */
    iwrite_elend(ptr_clrequest, "SINGLEPAGE");
  }

  if ((pagespagerangetype && *pagespagerangetype)
      || (pagespagerangepreceeding && *pagespagerangepreceeding)
      || (pagespagerangefollowing && *pagespagerangefollowing)
      || (pagespagerangerangeseparator && *pagespagerangerangeseparator)) {
    /* start pagerange block */
    if (insert_liliform(&sentinel, "TYPE", pagespagerangetype)) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return 1;
    }
    iwrite_elstart(ptr_clrequest, "PAGERANGE", &sentinel, 0);

    delete_all_liliform(&sentinel);

    /* preceeding */
    iwrite_element(ptr_clrequest, "PRECEEDING", NULL, pagespagerangepreceeding);
    /* rangesep */
    iwrite_element(ptr_clrequest, "RANGESEPARATOR", NULL, pagespagerangerangeseparator);

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

    /* end pagerange block */
    iwrite_elend(ptr_clrequest, "PAGERANGE");
  }
  
  iwrite_elend(ptr_clrequest, "PAGES");

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_pubdate() writes a pubdate element to a file descriptor.

  static int iwrite_pubdate 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  const char* role ptr to string with the authorlist role

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_pubdate(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role) {
  char pubdatefields[3][11][25] = {
    {"PUBDATEYEARFORMAT", "PUBDATEMONTHFORMAT", "PUBDATEFORMAT", "PUBDATESTYLE", "PUBDATEPADLEADINGZERO", "PUBDATEDAYFORMAT", "PUBDATESEQUENCE", "PUBDATEPRECEEDING", "PUBDATEFOLLOWING", "PUBDATEFIRSTSEP", "PUBDATESECONDSEP"},
    {"PUBDATESECYEARFORMAT", "PUBDATESECMONTHFORMAT", "PUBDATESECFORMAT", "PUBDATESECSTYLE", "PUBDATESECPADLEADINGZERO", "PUBDATESECDAYFORMAT", "PUBDATESECSEQUENCE", "PUBDATESECPRECEEDING", "PUBDATESECFOLLOWING", "PUBDATESECFIRSTSEP", "PUBDATESECSECONDSEP"},
    {"PUBDATEALLYEARFORMAT", "PUBDATEALLMONTHFORMAT", "PUBDATEALLFORMAT", "PUBDATEALLSTYLE", "PUBDATEALLPADLEADINGZERO", "PUBDATEALLDAYFORMAT", "PUBDATEALLSEQUENCE", "PUBDATEALLPRECEEDING", "PUBDATEALLFOLLOWING", "PUBDATEALLFIRSTSEP", "PUBDATEALLSECONDSEP"}
  };

  int n_role = 0;

  Liliform sentinel;

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

  if (!strcmp(role, "PRIMARY")) {
    n_role = 0;
  }
  else if (!strcmp(role, "SECONDARY")) {
    n_role = 1;
  }
  else if (!strcmp(role, "ALL")) {
    n_role = 2;
  }

  if (insert_liliform(&sentinel, "YEARFORMAT", (char*)my_dbi_result_get_string(dbires_ref, pubdatefields[n_role][0]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "MONTHFORMAT", (char*)my_dbi_result_get_string(dbires_ref, pubdatefields[n_role][1]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "FORMAT", (char*)my_dbi_result_get_string(dbires_ref, pubdatefields[n_role][2]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "STYLE", (char*)my_dbi_result_get_string(dbires_ref, pubdatefields[n_role][3]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "ROLE", (char*)role)) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "PADLEADINGZERO", (char*)my_dbi_result_get_string(dbires_ref, pubdatefields[n_role][4]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "DAYFORMAT", (char*)my_dbi_result_get_string(dbires_ref, pubdatefields[n_role][5]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "SEQUENCE", (char*)my_dbi_result_get_string(dbires_ref, pubdatefields[n_role][6]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  iwrite_elstart(ptr_clrequest, "PUBDATE", &sentinel, 0);

  delete_all_liliform(&sentinel);

  /* preceeding */
  iwrite_element(ptr_clrequest, "PRECEEDING", NULL, my_dbi_result_get_string(dbires_ref, pubdatefields[n_role][7]));

  /* following */
  iwrite_element(ptr_clrequest, "FOLLOWING", NULL, my_dbi_result_get_string(dbires_ref, pubdatefields[n_role][8]));

  /* firstsep */
  iwrite_element(ptr_clrequest, "FIRSTSEP", NULL, my_dbi_result_get_string(dbires_ref, pubdatefields[n_role][9]));

  /* secondsep */
  iwrite_element(ptr_clrequest, "SECONDSEP", NULL, my_dbi_result_get_string(dbires_ref, pubdatefields[n_role][10]));

  iwrite_elend(ptr_clrequest, "PUBDATE");

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_separator() writes a separator element to a file descriptor.

  static int iwrite_separator 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  unsigned int separator_id id of the separator to format

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_separator(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, unsigned int separator_id) {
  dbi_conn conn;
  dbi_result dbires;
  char sql_command[256];

  conn = dbi_result_get_conn(dbires_ref);

  sprintf(sql_command, "SELECT VALUE FROM SEPARATORS WHERE ID=%u", separator_id);
  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 */
      iwrite_element(ptr_clrequest, "SEPARATOR", NULL, my_dbi_result_get_string(dbires, "VALUE"));
    }
    dbi_result_free(dbires);
  }
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_title() writes a title element to a file descriptor.

  static int iwrite_title 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  const char* role ptr to string with the authorlist role

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_title(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* role) {
  char titlefields[4][4][22] = {
    {"TITLEPRECEEDING", "TITLEFOLLOWING", "TITLECASE", "TITLESTYLE"},
    {"BOOKTITLEPRECEEDING", "BOOKTITLEFOLLOWING", "BOOKTITLECASE", "BOOKTITLESTYLE"},
    {"SERIESTITLEPRECEEDING", "SERIESTITLEFOLLOWING", "SERIESTITLECASE", "SERIESTITLESTYLE"},
    {"ALLTITLEPRECEEDING", "ALLTITLEFOLLOWING", "ALLTITLECASE", "ALLTITLESTYLE"}
  };

  int n_role = 0;

  Liliform sentinel;

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

  if (!strcmp(role, "PART")) {
    n_role = 0;
  }
  else if (!strcmp(role, "PUB")) {
    n_role = 1;
  }
  else if (!strcmp(role, "SERIES")) {
    n_role = 2;
  }
  else if (!strcmp(role, "ALL")) {
    n_role = 3;
  }

  if (insert_liliform(&sentinel, "CASE", (char*)my_dbi_result_get_string(dbires_ref, titlefields[n_role][2]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  if (insert_liliform(&sentinel, "STYLE", (char*)my_dbi_result_get_string(dbires_ref, titlefields[n_role][3]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    delete_all_liliform(&sentinel);
    return 1;
  }

  if (insert_liliform(&sentinel, "ROLE", (char*)role)) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    delete_all_liliform(&sentinel);
    return 1;
  }

  iwrite_elstart(ptr_clrequest, "TITLE", &sentinel, 0);

  delete_all_liliform(&sentinel);

  /* preceeding */
  iwrite_element(ptr_clrequest, "PRECEEDING", NULL, my_dbi_result_get_string(dbires_ref, titlefields[n_role][0]));

  /* following */
  iwrite_element(ptr_clrequest, "FOLLOWING", NULL, my_dbi_result_get_string(dbires_ref, titlefields[n_role][1]));

  iwrite_elend(ptr_clrequest, "TITLE");

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_simple() writes a simple element to a file descriptor.

  static int iwrite_simple 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  const char* elname

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_simple(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* elname) {
  Liliform sentinel;
  char buffer[64];

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

  snprintf(buffer, 64, "%sSTYLE", elname);

  if (insert_liliform(&sentinel, "STYLE", (char*)my_dbi_result_get_string(dbires_ref, buffer))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return 1;
  }

  iwrite_elstart(ptr_clrequest, elname, &sentinel, 0);

  delete_all_liliform(&sentinel);

  /* preceeding */
  snprintf(buffer, 64, "%sPRECEEDING", elname);
  iwrite_element(ptr_clrequest, "PRECEEDING", NULL, my_dbi_result_get_string(dbires_ref, buffer));

  /* following */
  snprintf(buffer, 64, "%sFOLLOWING", elname);
  iwrite_element(ptr_clrequest, "FOLLOWING", NULL, my_dbi_result_get_string(dbires_ref, buffer));

  iwrite_elend(ptr_clrequest, elname);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  iwrite_twoatt() writes an element with two attributes to a file descriptor.

  static int iwrite_twoatt 0 if fine, 1 if some error occurred

  struct CLIENT_REQUEST* ptr_clrequest ptr to a structure with client info

  dbi_result dbires_ref ptr to citstyle query result structure

  const char* elname ptr to string with element name

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int iwrite_twoatt(struct CLIENT_REQUEST* ptr_clrequest, dbi_result dbires_ref, const char* elname) {
  char my_elname[10];
  char fields[10][4][20] = {
    {"USERDEF1PRECEEDING", "USERDEF1FOLLOWING", "USERDEF1STYLE", "1"},
    {"USERDEF2PRECEEDING", "USERDEF2FOLLOWING", "USERDEF2STYLE", "2"},
    {"USERDEF3PRECEEDING", "USERDEF3FOLLOWING", "USERDEF3STYLE", "3"},
    {"USERDEF4PRECEEDING", "USERDEF4FOLLOWING", "USERDEF4STYLE", "4"},
    {"USERDEF5PRECEEDING", "USERDEF5FOLLOWING", "USERDEF5STYLE", "5"},
    {"LINK0PRECEEDING", "LINK0FOLLOWING", "LINK0STYLE", "0"},
    {"LINK1PRECEEDING", "LINK1FOLLOWING", "LINK1STYLE", "1"},
    {"LINK2PRECEEDING", "LINK2FOLLOWING", "LINK2STYLE", "2"},
    {"LINK3PRECEEDING", "LINK3FOLLOWING", "LINK3STYLE", "3"},
    {"LINK4PRECEEDING", "LINK4FOLLOWING", "LINK4STYLE", "4"},
  };

  int n_role;

  Liliform sentinel;

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

  if (!strncmp(elname, "USERDEF", 7)) {
    n_role = atoi(elname+7) - 1; /* attributes are 1-based */
    strcpy(my_elname, "USERDEF");
  }
  else if (!strncmp(elname, "LINK", 4)) {
    n_role = atoi(elname+4) + 5;
    strcpy(my_elname, "LINK");
  }
  else {
    return 1; /* should never happen */
  }

  if (insert_liliform(&sentinel, "STYLE", (char*)my_dbi_result_get_string(dbires_ref, fields[n_role][2]))) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    delete_all_liliform(&sentinel);
    return 1;
  }

  if (insert_liliform(&sentinel, "ROLE", fields[n_role][3])) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    delete_all_liliform(&sentinel);
    return 1;
  }

  iwrite_elstart(ptr_clrequest, my_elname, &sentinel, 0);

  delete_all_liliform(&sentinel);

  /* preceeding */
  iwrite_element(ptr_clrequest, "PRECEEDING", NULL, my_dbi_result_get_string(dbires_ref, fields[n_role][0]));

  /* following */
  iwrite_element(ptr_clrequest, "FOLLOWING", NULL, my_dbi_result_get_string(dbires_ref, fields[n_role][1]));

  iwrite_elend(ptr_clrequest, my_elname);

  return 0;
}



