/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  backend-dbiba.c defines the DocBook V.3.1 bibliography output backend
  of refdbd (helper functions)
  markus@mhoenicka.de 2001-01-18

   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 <string.h>
#include <stdio.h>
#include <dbi/dbi.h>
#include <syslog.h>
#include <ctype.h> /* for isdigit() */
#include <limits.h> /* for MAX_INT */

#include "refdb.h"
#include "backend.h"
#include "xmlhelper.h"
#include "strfncs.h"
#include "linklist.h"
#include "refdbd.h"
#include "backend-dbib.h"
#include "tokenize.h"
#include "dbfncs.h"
#include "risdb.h"
#include "authorinfo.h"
#include "connect.h"
#include "mset.h"
#include "backend-rtfbib.h"
#include "rtfhelper.h"

extern int n_log_level;
extern char main_db[];


/* forward declarations of local functions */
static char* arabic_to_roman(char* roman, const char* arabic);
static int format_authorname(char** ptr_ref, size_t* ptr_ref_len, struct AUTHOR_INFO* ptr_ainfo, const char* authorsep, const char* nameorder, const char* initialstyle, const char* author_upper, int nis_intext, int type, const char* ns, struct xmlindent* ptr_indent, int n_ref_format, int* ptr_trailing_dot);
static char* format_day(char* ref, size_t* ptr_ref_len, const char* day, const char* dayformat, const char* pad);
static char* format_firstmiddlename(char** ptr_ref, size_t* ptr_ref_len, struct AUTHOR_INFO* ptr_ainfo, const char* author_upper, const char* initialstyle, int nis_intext, int type, const char* ns, struct xmlindent* ptr_indent, int n_ref_format, int* ptr_trailing_dot);
static char* format_lastname(char** ptr_ref, size_t* ptr_ref_len, char* lastname, const char* author_upper, int nis_intext, int type, const char* ns, struct xmlindent* ptr_indent, int* ptr_trailing_dot, int n_ref_format);
static char* format_month(char* ref, size_t* ptr_ref_len, const char* month, const char* monthformat, const char* pad, dbi_result dbirescit);
static char* format_year(char* ref, size_t* ptr_ref_len, unsigned short n_year, const char* yearformat, const char* pad, const char* unique_suffix);
static char* normalize_pages(char* new_endpage, const char* startpage, const char* endpage, const char* format);

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_refnumber(): formats the refnumber part of a bibliography entry
                      as a DocBook bibliomixed text

  char* format_refnumber returns ptr to the buffer if successful, NULL if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least 4096 byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  struct BIBCONNS* ptr_bibconns ptr to structure with database connections

  dbi_result dbires ptr to a dbi result structure containing the
                    current reference

  dbi_result dbires_ref ptr to a row containing the pubtype style

  int nref_counter number of current dataset in bibliography sequence

  int n_intext_subseq will not be used unless pubtype=INTEXT. Set to 1
                  to format the intext citation for subsequent citations
                  of the same publication. Set to 0 to format the first
                  citation

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* format_refnumber(char** ptr_ref, size_t* ptr_ref_len, struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_ref, int nref_counter, int n_intext_subseq, const char* ns, struct xmlindent* ptr_indent, int n_ref_format) {
  char* new_ref;
  char* item;
  char buffer[16];
/*   dbi_conn conn; */

/*   conn = dbi_result_get_conn(dbires_ref); */

  if (n_ref_format == REFTEIX5) {
    if (print_elstart_x(ptr_ref, ptr_ref_len, "abbr", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
      return NULL;
    }
  }
  else if (n_ref_format == REFRTF) {
    if (print_rtf_format(ptr_ref, ptr_ref_len, "REFNUMBER", dbires_ref, 1 /* start group */) == NULL) {
      return NULL;
    }
  }
  else {
    if (print_elstart_x(ptr_ref, ptr_ref_len, "abbrev", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
      return NULL;
    }
  }

  /* get the result string in an allocated buffer */
  item = my_dbi_result_get_string_copy_idx(dbires_ref, REFNUMBERPRECEEDING);
  if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
    if (sgml_entitize(&item, n_ref_format) == NULL) {
      free(item);
      return NULL;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
      free(item);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  if (item) {
    free(item);
  }

  sprintf(buffer, "%d", nref_counter);
  if ((new_ref = mstrcat(*ptr_ref, buffer, ptr_ref_len, 0)) == NULL) {
    return NULL;
  }
  else {
    *ptr_ref = new_ref;
  }
 
  item = my_dbi_result_get_string_copy_idx(dbires_ref, REFNUMBERFOLLOWING);
  if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* following */
    if (sgml_entitize(&item, n_ref_format) == NULL) {
      free(item);
      return NULL;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
      free(item);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  if (item) {
    free(item);
  }

  if (n_ref_format == REFTEIX5) {
    if (print_elend_x(ptr_ref, ptr_ref_len, "abbr", ptr_indent, ns) == NULL) {
      return NULL;
    }
  }
  else if (n_ref_format == REFRTF) {
    if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
      return NULL;
    }
  }
  else {
    if (print_elend_x(ptr_ref, ptr_ref_len, "abbrev", ptr_indent, ns) == NULL) {
      return NULL;
    }
  }

  return *ptr_ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_citekey(): formats the citekey part of a bibliography entry
                      as a DocBook bibliomixed text

  char* format_citekey returns ptr to the buffer if successful, NULL if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least 4096 byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  struct BIBCONNS* ptr_bibconns ptr to structure with database connections

  dbi_result dbires ptr to a dbi result structure containing the
                    current reference

  dbi_result dbires_ref ptr to a row containing the pubtype style

  int n_intext_subseq will not be used unless pubtype=INTEXT. Set to 1
                  to format the intext citation for subsequent citations
                  of the same publication. Set to 0 to format the first
                  citation

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* format_citekey(char** ptr_ref, size_t* ptr_ref_len, struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_ref, int n_intext_subseq, const char* ns, struct xmlindent* ptr_indent, int n_ref_format) {
  char* new_ref;
  char* item;

  if (n_ref_format == REFTEIX5) {
    if (print_elstart_x(ptr_ref, ptr_ref_len, "abbr", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
      return NULL;
    }
  }
  else if (n_ref_format == REFRTF) {
    if (print_rtf_format(ptr_ref, ptr_ref_len, "CITEKEY", dbires_ref, 1 /* start group */) == NULL) {
      return NULL;
    }
  }
  else {
    if (print_elstart_x(ptr_ref, ptr_ref_len, "abbrev", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
      return NULL;
    }
  }

  /* get the result string in an allocated buffer */
  item = my_dbi_result_get_string_copy_idx(dbires_ref, CITEKEYPRECEEDING);
  if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
    if (sgml_entitize(&item, n_ref_format) == NULL) {
      free(item);
      return NULL;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
      free(item);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  if (item) {
    free(item);
  }

  /* get citation key */
  item = my_dbi_result_get_string_copy_idx(dbires, REFDB_CITEKEY);
  if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
    if (sgml_entitize(&item, n_ref_format) == NULL) {
      free(item);
      return NULL;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
      free(item);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  if (item) {
    free(item);
  }

 
  item = my_dbi_result_get_string_copy_idx(dbires_ref, CITEKEYFOLLOWING);
  if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* following */
    if (sgml_entitize(&item, n_ref_format) == NULL) {
      free(item);
      return NULL;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
      free(item);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  if (item) {
    free(item);
  }

  if (n_ref_format == REFTEIX5) {
    if (print_elend_x(ptr_ref, ptr_ref_len, "abbr", ptr_indent, ns) == NULL) {
      return NULL;
    }
  }
  else if (n_ref_format == REFRTF) {
    if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
      return NULL;
    }
  }
  else {
    if (print_elend_x(ptr_ref, ptr_ref_len, "abbrev", ptr_indent, ns) == NULL) {
      return NULL;
    }
  }

  return *ptr_ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_authorlist(): formats the author/editor/seditorlist part of a
                      bibliography entry as a DocBook bibliomixed text

  int format_authorlist returns 0 if successful, >0 if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  struct BIBCONNS* ptr_bibconns ptr to struct with dbi connections

  dbi_result dbires ptr to a dbi result structure containing the current
                    reference. This may be NULL if the refdb_id is
		    provided in the ptr_biblio_info structure

  dbi_result dbires_ref ptr to a dbi result structure containing the
                    pubtype style

  int type 1=part authors, 2=publication authors, 3=series authors 
           4=first available

  const char* database ptr to string containing the database with the
                       author data

  int n_intext  0 = bibliography citation; 1 = intext citation; 2 =
                    intext citation subsequent

  struct bibinfo* ptr_biblio_info ptr to struct with additional bibliographic
                  info

  short title_as_author if >0 the reference has no authors and the style
                  expects the title to be printed instead
		  1=title, 2=booktitle

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information (XML only, use NULL
                                                         otherwise)

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX,
                                            REFTEIX, REFRTF

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int format_authorlist(char** ptr_ref, size_t* ptr_ref_len, struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_ref, int type, const char* database, int n_intext, struct bibinfo* ptr_biblio_info, short title_as_author, const char* ns, struct xmlindent* ptr_indent, int n_ref_format) {
  int num_authors;
  int nauthor_count = 0;
  int nauthor_max;
  int nauthor_display;
  int nhave_content = 0;
  int n_status;
  int n_have_editor = 0;
  int must_free_dbires = 0;
  int n_trailing_dot = 0;
  unsigned long long n_id;
  const char *authorsep;
  const char *nameorder;
  const char *initialstyle;
  const char *author_upper;
  const char *citem;
  const char *alternatetext = NULL;
  char *author_anonymous = NULL;
  char *author_preceeding = NULL;
  char *author_following = NULL;
  char *alist_preceeding = NULL;
  char *new_ref;
  char *item;
  char *item1;
  char *the_refdb_id;
  char id[32] = "";
  struct AUTHOR_INFO ainfo;

  dbi_result dbires_author;
  enum refdb_col n_preceeding_index;
  enum refdb_col n_following_index;
  enum refdb_col n_abbreviate_first_index;
  enum refdb_col n_abbreviate_max_first_index;
  enum refdb_col n_abbreviate_display_first_index;
  enum refdb_col n_abbreviate_subseq_index;
  enum refdb_col n_abbreviate_max_subseq_index;
  enum refdb_col n_abbreviate_display_subseq_index;
  enum refdb_col n_authorempty_index;
  enum refdb_col n_alternatetext_index;
  enum refdb_col n_asame_index;
  enum refdb_col n_twoseps_index;
  enum refdb_col n_threesepseach_index;
  enum refdb_col n_threesepslast_index;
  enum refdb_col n_namefirstorder_index;
  enum refdb_col n_namefirstinitial_index;
  enum refdb_col n_namefirstupper_index;
  enum refdb_col n_nameotherorder_index;
  enum refdb_col n_nameotherinitial_index;
  enum refdb_col n_nameotherupper_index;
  enum refdb_col n_singlepreceeding_index;
  enum refdb_col n_singlefollowing_index;
  enum refdb_col n_multiplepreceeding_index;
  enum refdb_col n_multiplefollowing_index;
  enum refdb_col n_edsinglepreceeding_index;
  enum refdb_col n_edsinglefollowing_index;
  enum refdb_col n_edmultiplepreceeding_index;
  enum refdb_col n_edmultiplefollowing_index;
  enum refdb_col n_my_singlepreceeding_index;
  enum refdb_col n_my_singlefollowing_index;
  enum refdb_col n_my_multiplepreceeding_index;
  enum refdb_col n_my_multiplefollowing_index;

/*   printf("start format_authorlist(), type went to %d\n", type); */

  /* map the database query result columns to index variables according to the type of authorlist */
  if (type == 1) { /* part authors */
    n_preceeding_index = AUTHORLISTPRECEEDING;
    n_following_index = AUTHORLISTFOLLOWING;
    n_abbreviate_first_index = AUTHORLISTABBREVIATEFIRST;
    n_abbreviate_max_first_index = AUTHORLISTABBREVIATEFIRSTMAXAUTHOR;
    n_abbreviate_display_first_index = AUTHORLISTABBREVIATEFIRSTDISPLAYAUTHOR;
    n_abbreviate_subseq_index = AUTHORLISTABBREVIATESUBSEQ;
    n_abbreviate_max_subseq_index = AUTHORLISTABBREVIATESUBSEQMAXAUTHOR;
    n_abbreviate_display_subseq_index = AUTHORLISTABBREVIATESUBSEQDISPLAYAUTHOR;
    n_authorempty_index = AUTHORLISTAEMPTY;
    n_alternatetext_index = AUTHORLISTALTERNATETEXT;
    n_asame_index = AUTHORLISTASAME;
    n_twoseps_index = AUTHORLISTAUTHORSEPSTWOSEPS;
    n_threesepseach_index = AUTHORLISTAUTHORSEPSTHREESEPSTHREESEPSEACH;
    n_threesepslast_index = AUTHORLISTAUTHORSEPSTHREESEPSTHREESEPSLAST;
    n_namefirstorder_index = AUTHORLISTAUTHORNAMESNAMEFIRSTNAMEORDER;
    n_namefirstinitial_index = AUTHORLISTAUTHORNAMESNAMEFIRSTINITIALSTYLE;
    n_namefirstupper_index = AUTHORLISTAUTHORNAMESNAMEFIRSTUPPERCASE;
    n_nameotherorder_index = AUTHORLISTAUTHORNAMESNAMEOTHERNAMEORDER;
    n_nameotherinitial_index = AUTHORLISTAUTHORNAMESNAMEOTHERINITIALSTYLE;
    n_nameotherupper_index = AUTHORLISTAUTHORNAMESNAMEOTHERUPPERCASE;
    n_singlepreceeding_index = AUTHORLISTTEXTTEXTSINGLEPRECEEDING;
    n_singlefollowing_index = AUTHORLISTTEXTTEXTSINGLEFOLLOWING;
    n_multiplepreceeding_index = AUTHORLISTTEXTTEXTMULTIPLEPRECEEDING;
    n_multiplefollowing_index = AUTHORLISTTEXTTEXTMULTIPLEFOLLOWING;
    n_edsinglepreceeding_index = AUTHORLISTTEXTEDTEXTSINGLEPRECEEDING;
    n_edsinglefollowing_index = AUTHORLISTTEXTEDTEXTSINGLEFOLLOWING;
    n_edmultiplepreceeding_index = AUTHORLISTTEXTEDTEXTMULTIPLEPRECEEDING;
    n_edmultiplefollowing_index = AUTHORLISTTEXTEDTEXTMULTIPLEFOLLOWING;
  }
  else if (type == 2) { /* publication authors */
    n_preceeding_index = EDITORLISTPRECEEDING;
    n_following_index = EDITORLISTFOLLOWING;
    n_abbreviate_first_index = EDITORLISTABBREVIATEFIRST;
    n_abbreviate_max_first_index = EDITORLISTABBREVIATEFIRSTMAXAUTHOR;
    n_abbreviate_display_first_index = EDITORLISTABBREVIATEFIRSTDISPLAYAUTHOR;
    n_abbreviate_subseq_index = EDITORLISTABBREVIATESUBSEQ;
    n_abbreviate_max_subseq_index = EDITORLISTABBREVIATESUBSEQMAXAUTHOR;
    n_abbreviate_display_subseq_index = EDITORLISTABBREVIATESUBSEQDISPLAYAUTHOR;
    n_authorempty_index = EDITORLISTAEMPTY;
    n_alternatetext_index = EDITORLISTALTERNATETEXT;
    n_asame_index = EDITORLISTASAME;
    n_twoseps_index = EDITORLISTAUTHORSEPSTWOSEPS;
    n_threesepseach_index = EDITORLISTAUTHORSEPSTHREESEPSTHREESEPSEACH;
    n_threesepslast_index = EDITORLISTAUTHORSEPSTHREESEPSTHREESEPSLAST;
    n_namefirstorder_index = EDITORLISTAUTHORNAMESNAMEFIRSTNAMEORDER;
    n_namefirstinitial_index = EDITORLISTAUTHORNAMESNAMEFIRSTINITIALSTYLE;
    n_namefirstupper_index = EDITORLISTAUTHORNAMESNAMEFIRSTUPPERCASE;
    n_nameotherorder_index = EDITORLISTAUTHORNAMESNAMEOTHERNAMEORDER;
    n_nameotherinitial_index = EDITORLISTAUTHORNAMESNAMEOTHERINITIALSTYLE;
    n_nameotherupper_index = EDITORLISTAUTHORNAMESNAMEOTHERUPPERCASE;
    n_singlepreceeding_index = EDITORLISTTEXTTEXTSINGLEPRECEEDING;
    n_singlefollowing_index = EDITORLISTTEXTTEXTSINGLEFOLLOWING;
    n_multiplepreceeding_index = EDITORLISTTEXTTEXTMULTIPLEPRECEEDING;
    n_multiplefollowing_index = EDITORLISTTEXTTEXTMULTIPLEFOLLOWING;
    n_edsinglepreceeding_index = EDITORLISTTEXTEDTEXTSINGLEPRECEEDING;
    n_edsinglefollowing_index = EDITORLISTTEXTEDTEXTSINGLEFOLLOWING;
    n_edmultiplepreceeding_index = EDITORLISTTEXTEDTEXTMULTIPLEPRECEEDING;
    n_edmultiplefollowing_index = EDITORLISTTEXTEDTEXTMULTIPLEFOLLOWING;
  }
  else if (type == 3) { /* series authors */
    n_preceeding_index = SEDITORLISTPRECEEDING;
    n_following_index = SEDITORLISTFOLLOWING;
    n_abbreviate_first_index = SEDITORLISTABBREVIATEFIRST;
    n_abbreviate_max_first_index = SEDITORLISTABBREVIATEFIRSTMAXAUTHOR;
    n_abbreviate_display_first_index = SEDITORLISTABBREVIATEFIRSTDISPLAYAUTHOR;
    n_abbreviate_subseq_index = SEDITORLISTABBREVIATESUBSEQ;
    n_abbreviate_max_subseq_index = SEDITORLISTABBREVIATESUBSEQMAXAUTHOR;
    n_abbreviate_display_subseq_index = SEDITORLISTABBREVIATESUBSEQDISPLAYAUTHOR;
    n_authorempty_index = SEDITORLISTAEMPTY;
    n_alternatetext_index = SEDITORLISTALTERNATETEXT;
    n_asame_index = SEDITORLISTASAME;
    n_twoseps_index = SEDITORLISTAUTHORSEPSTWOSEPS;
    n_threesepseach_index = SEDITORLISTAUTHORSEPSTHREESEPSTHREESEPSEACH;
    n_threesepslast_index = SEDITORLISTAUTHORSEPSTHREESEPSTHREESEPSLAST;
    n_namefirstorder_index = SEDITORLISTAUTHORNAMESNAMEFIRSTNAMEORDER;
    n_namefirstinitial_index = SEDITORLISTAUTHORNAMESNAMEFIRSTINITIALSTYLE;
    n_namefirstupper_index = SEDITORLISTAUTHORNAMESNAMEFIRSTUPPERCASE;
    n_nameotherorder_index = SEDITORLISTAUTHORNAMESNAMEOTHERNAMEORDER;
    n_nameotherinitial_index = SEDITORLISTAUTHORNAMESNAMEOTHERINITIALSTYLE;
    n_nameotherupper_index = SEDITORLISTAUTHORNAMESNAMEOTHERUPPERCASE;
    n_singlepreceeding_index = SEDITORLISTTEXTTEXTSINGLEPRECEEDING;
    n_singlefollowing_index = SEDITORLISTTEXTTEXTSINGLEFOLLOWING;
    n_multiplepreceeding_index = SEDITORLISTTEXTTEXTMULTIPLEPRECEEDING;
    n_multiplefollowing_index = SEDITORLISTTEXTTEXTMULTIPLEFOLLOWING;
    n_edsinglepreceeding_index = SEDITORLISTTEXTEDTEXTSINGLEPRECEEDING;
    n_edsinglefollowing_index = SEDITORLISTTEXTEDTEXTSINGLEFOLLOWING;
    n_edmultiplepreceeding_index = SEDITORLISTTEXTEDTEXTMULTIPLEPRECEEDING;
    n_edmultiplefollowing_index = SEDITORLISTTEXTEDTEXTMULTIPLEFOLLOWING;
  }
  else { /* allalist */
    n_preceeding_index = ALLALISTPRECEEDING;
    n_following_index = ALLALISTFOLLOWING;
    n_abbreviate_first_index = ALLALISTABBREVIATEFIRST;
    n_abbreviate_max_first_index = ALLALISTABBREVIATEFIRSTMAXAUTHOR;
    n_abbreviate_display_first_index = ALLALISTABBREVIATEFIRSTDISPLAYAUTHOR;
    n_abbreviate_subseq_index = ALLALISTABBREVIATESUBSEQ;
    n_abbreviate_max_subseq_index = ALLALISTABBREVIATESUBSEQMAXAUTHOR;
    n_abbreviate_display_subseq_index = ALLALISTABBREVIATESUBSEQDISPLAYAUTHOR;
    n_authorempty_index = ALLALISTAEMPTY;
    n_alternatetext_index = ALLALISTALTERNATETEXT;
    n_asame_index = ALLALISTASAME;
    n_twoseps_index = ALLALISTAUTHORSEPSTWOSEPS;
    n_threesepseach_index = ALLALISTAUTHORSEPSTHREESEPSTHREESEPSEACH;
    n_threesepslast_index = ALLALISTAUTHORSEPSTHREESEPSTHREESEPSLAST;
    n_namefirstorder_index = ALLALISTAUTHORNAMESNAMEFIRSTNAMEORDER;
    n_namefirstinitial_index = ALLALISTAUTHORNAMESNAMEFIRSTINITIALSTYLE;
    n_namefirstupper_index = ALLALISTAUTHORNAMESNAMEFIRSTUPPERCASE;
    n_nameotherorder_index = ALLALISTAUTHORNAMESNAMEOTHERNAMEORDER;
    n_nameotherinitial_index = ALLALISTAUTHORNAMESNAMEOTHERINITIALSTYLE;
    n_nameotherupper_index = ALLALISTAUTHORNAMESNAMEOTHERUPPERCASE;
    n_singlepreceeding_index = ALLALISTTEXTTEXTSINGLEPRECEEDING;
    n_singlefollowing_index = ALLALISTTEXTTEXTSINGLEFOLLOWING;
    n_multiplepreceeding_index = ALLALISTTEXTTEXTMULTIPLEPRECEEDING;
    n_multiplefollowing_index = ALLALISTTEXTTEXTMULTIPLEFOLLOWING;
    n_edsinglepreceeding_index = ALLALISTTEXTEDTEXTSINGLEPRECEEDING;
    n_edsinglefollowing_index = ALLALISTTEXTEDTEXTSINGLEFOLLOWING;
    n_edmultiplepreceeding_index = ALLALISTTEXTEDTEXTMULTIPLEPRECEEDING;
    n_edmultiplefollowing_index = ALLALISTTEXTEDTEXTMULTIPLEFOLLOWING;
  }

  /* initialize struct */
  *(ainfo.name) = '\0';
  *(ainfo.lastname) = '\0';
  *(ainfo.firstname) = '\0';
  *(ainfo.middlename) = '\0';
  *(ainfo.suffix) = '\0';

  /* ToDo: fix the n_id/id/the_refdb_id/whatever kludge */
  if (dbires) {
    n_id = my_dbi_result_get_idval(dbires, "refdb_id");
    if (!n_id) {
      return 244;
    }
    sprintf(id, ULLSPEC, (unsigned long long)n_id);
    the_refdb_id = id;
  }
  else {
    sprintf(id, ULLSPEC, (unsigned long long)(ptr_biblio_info->n_refdb_id));
    the_refdb_id = id;
    n_id = ptr_biblio_info->n_refdb_id;
  }

/*   conn = dbi_result_get_conn(dbires_ref); */

  if ((!n_intext && ptr_biblio_info->is_subseq) || (n_intext == 2)) {
    citem = dbi_result_get_string_idx(dbires_ref, n_abbreviate_max_subseq_index);
    if (citem && *citem && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
      nauthor_max = atoi((char*)citem);
      nauthor_max = (nauthor_max < 1) ? 1 : nauthor_max;
    }
    else {
      nauthor_max = INT_MAX;
    }

    citem = dbi_result_get_string_idx(dbires_ref, n_abbreviate_display_subseq_index);
    if (citem && *citem && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
      nauthor_display = atoi((char*)citem);
      nauthor_display = (nauthor_display < 1) ? 1 : nauthor_display;
    }
    else {
      nauthor_display = INT_MAX;
    }
  }
  else {
    citem = dbi_result_get_string_idx(dbires_ref, n_abbreviate_max_first_index);
    if (citem && *citem && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
      nauthor_max = atoi((char*)citem);
      nauthor_max = (nauthor_max < 1) ? 1 : nauthor_max;
    }
    else {
      nauthor_max = INT_MAX;
    }

    citem = dbi_result_get_string_idx(dbires_ref, n_abbreviate_display_first_index);
    if (citem && *citem && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
      nauthor_display = atoi((char*)citem);
      nauthor_display = (nauthor_display < 1) ? 1 : nauthor_display;
    }
    else {
      nauthor_display = INT_MAX;
    }
  }

  /* check for subsequent reference of same author(s) */
  if ((!n_intext && ptr_biblio_info->is_subseq) || (n_intext == 2)) {
    item = my_dbi_result_get_string_copy_idx(dbires_ref, n_asame_index);
    if (item && !*item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
      free(item);
      return 0; /* ASAME is empty string -> suppress authorlist */
    }
    else if (item) {
      item1 = my_dbi_result_get_string_copy_idx(dbires_ref, n_preceeding_index);
      if (item1 && *item1 && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
	if (sgml_entitize(&item1, n_ref_format) == NULL) {
	  free(item);
	  free(item1);
	  return 801;
	}
      
	if ((new_ref = mstrcat(*ptr_ref, item1, ptr_ref_len, 0)) == NULL) {
	  free(item1);
	  free(item);
	  return 801;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      if (item1) { /* found, but empty */
	free(item1);
      }

      item1 = my_dbi_result_get_string_copy_idx(dbires_ref, n_singlepreceeding_index);
      if (item1 && *item1 && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* textpreceeding */
	if (sgml_entitize(&item1, n_ref_format) == NULL) {
	  free(item1);
	  free(item);
	  return 801;
	}
      
	if ((new_ref = mstrcat(*ptr_ref, item1, ptr_ref_len, 0)) == NULL) {
	  free(item1);
	  free(item);
	  return 801;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      if (item1) { /* found but empty */
	free(item1);
      }

      /* item contains asame */
      if (sgml_entitize(&item, n_ref_format) == NULL) {
	free(item);
	return 801;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	free(item);
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
      free(item);

      nhave_content = 1;

      /* reuse item */
      item = my_dbi_result_get_string_copy_idx(dbires_ref, n_following_index);
      if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* following */
	if (sgml_entitize(&item, n_ref_format) == NULL) {
	  free(item);
	  return 801;
	}
      
	if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	  free(item);
	  return 801;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      if (item) {
	free(item);
      }
      return 0;
    }
    /* else: ASAME undefined, process authorlist as usual */
  } /* end if is_subseq */

  if ((dbires_author = request_authors(ptr_bibconns->conn_source, type, NULL /* all roles */, database, 0, n_id)) != NULL) {
    while (get_author_parts(dbires_author, &ainfo) != NULL) {
      if (!strcmp(ainfo.role, "editor")) {
	n_have_editor++;
      }
    }
    dbi_result_free(dbires_author);
  }

  if (n_have_editor) {
    n_my_singlepreceeding_index = n_edsinglepreceeding_index;
    n_my_singlefollowing_index = n_edsinglefollowing_index;
    n_my_multiplepreceeding_index = n_edmultiplepreceeding_index;
    n_my_multiplefollowing_index = n_edmultiplefollowing_index;
  }
  else {
    n_my_singlepreceeding_index = n_singlepreceeding_index;
    n_my_singlefollowing_index = n_singlefollowing_index;
    n_my_multiplepreceeding_index = n_multiplepreceeding_index;
    n_my_multiplefollowing_index = n_multiplefollowing_index;
  }

  alist_preceeding = my_dbi_result_get_string_copy_idx(dbires_ref, n_preceeding_index);
  if (alist_preceeding && *alist_preceeding && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
    if (sgml_entitize(&alist_preceeding, n_ref_format) == NULL) {
      free(alist_preceeding);
      return 801;
    }
      
    /* this will be used later only if we insert some author data */
  }

  /* todo: check whether authors have a role attribute of editor, set preceeding and following indexes appropriately */
  if ((dbires_author = request_authors(ptr_bibconns->conn_source, type, NULL /* all roles */, database, 0, n_id)) != NULL) {

    /* depending on the total number of authors we need different preceedings and followings */
    num_authors = dbi_result_get_numrows(dbires_author);
/*     fprintf(stderr, "num_authors went to %d<<\n", num_authors); */
    if (num_authors > 1) {
      author_preceeding = my_dbi_result_get_string_copy_idx(dbires_ref, n_my_multiplepreceeding_index);
      if (author_preceeding && my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	free(author_preceeding);
	author_preceeding = NULL;
      }

      author_following = my_dbi_result_get_string_copy_idx(dbires_ref, n_my_multiplefollowing_index);
      if (author_following && my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	free(author_following);
	author_following = NULL;
      }
    }
    else if (num_authors == 0) {
      char sql_command[1024];
      
      alternatetext = my_dbi_result_get_string_idx(dbires_ref, n_alternatetext_index);
      if (alternatetext && my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	alternatetext = NULL;
      }

/*       printf("alternatetext went to %s<<\n", alternatetext); */
/*       if (title_as_author) { */
      if (alternatetext && strcmp(alternatetext, "AEMPTY")) {
	/* use title here if style requires it */

	/* todo: use alternatetext here */
	if (!dbires) {
	  /* when asking only for the authorlist, we don't have a result with the contents of t_refdb */
	  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);
	  dbires = dbi_conn_query(ptr_bibconns->conn_source, sql_command);
	  
	  if (!dbires || dbi_result_next_row(dbires) == 0) {
	    /* todo: exit gracefully */
	    LOG_PRINT(LOG_WARNING, "retrieve title instead of author failed");
	  }
	  must_free_dbires++;
	}

	if (dbires) {
	  const char* reftype;

	  reftype = my_dbi_result_get_string_idx(dbires, REFDB_TYPE);

	  /* returns 0 if type==NULL */
	  if (has_part_data(reftype)) {
	    title_as_author = 1;
	  }
	  else {
	    title_as_author = 2;
	  }
	}
	else {
	  title_as_author = 0;
	}
      }
      else { /* use AEMPTY instead of title */
	author_anonymous = my_dbi_result_get_string_copy_idx(dbires_ref, n_authorempty_index);
	if (author_anonymous && *author_anonymous && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	  if (sgml_entitize(&author_anonymous, n_ref_format) == NULL) {
	    free(author_anonymous);
	    free(alist_preceeding);
	    clean_request(dbires_author);
	    return 801;
	  }
      
	  author_preceeding = my_dbi_result_get_string_copy_idx(dbires_ref, n_my_singlepreceeding_index);
	  if (author_preceeding && my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	    free(author_preceeding);
	    author_preceeding = NULL;
	  }

	  author_following = my_dbi_result_get_string_copy_idx(dbires_ref, n_my_singlefollowing_index);
	  if (author_following && my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	    free(author_following);
	    author_following = NULL;
	  }
	}
	else {    /* no authors, go home */
	  if (author_anonymous) {
	    free(author_anonymous);
	  }
	  clean_request(dbires_author);
	  if (alist_preceeding) {
	    free(alist_preceeding);
	  }
	  return 0;
	}
      } /* end if title as author */
    }
    else { /* num_authors == 1 */
      author_preceeding = my_dbi_result_get_string_copy_idx(dbires_ref, n_my_singlepreceeding_index);
      if (author_preceeding && my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	free(author_preceeding);
	author_preceeding = NULL;
      }

      author_following = my_dbi_result_get_string_copy_idx(dbires_ref, n_my_singlefollowing_index);
      if (author_following && my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	free(author_following);
	author_following = NULL;
      }
    }

    /* list preceeding */

    if (alist_preceeding && *alist_preceeding) { /* preceeding */
      if ((new_ref = mstrcat(*ptr_ref, alist_preceeding, ptr_ref_len, 0)) == NULL) {
	free(alist_preceeding);
	if (author_anonymous) {
	  free(author_anonymous);
	}
	if (author_following) {
	  free(author_following);
	}
	clean_request(dbires_author);
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    if (alist_preceeding) {
      free(alist_preceeding);
    }

    /* text preceeding */
    if (author_preceeding && *author_preceeding) { /* preceeding */
      if (sgml_entitize(&author_preceeding, n_ref_format) == NULL) {
	free(author_preceeding);
	if (author_anonymous) {
	  free(author_anonymous);
	}
	if (author_following) {
	  free(author_following);
	}
	clean_request(dbires_author);
	return 801;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, author_preceeding, ptr_ref_len, 0)) == NULL) {
	free(author_preceeding);
	if (author_following) {
	  free(author_following);
	}
	clean_request(dbires_author);
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    if (author_preceeding) {
      free(author_preceeding);
    }


    if (author_anonymous) {
      strncpy(ainfo.name, author_anonymous, 255);
      ainfo.name[255] = '\0';

      nhave_content = 1;
      authorsep = NULL;
      nameorder = dbi_result_get_string_idx(dbires_ref, n_namefirstorder_index);
      initialstyle = dbi_result_get_string_idx(dbires_ref, n_namefirstinitial_index);
      author_upper = dbi_result_get_string_idx(dbires_ref, n_namefirstupper_index);
	
      if ((n_status = format_authorname(ptr_ref, ptr_ref_len, &ainfo, authorsep, nameorder, initialstyle, author_upper, n_intext, type, ns, ptr_indent, n_ref_format, &n_trailing_dot)) != 0) {
	clean_request(dbires_author);
	free(author_anonymous);
	if (author_following) {
	  free(author_following);
	}
	return n_status;
      }

      free(author_anonymous);
    }
    else if (title_as_author) {
      /* todo: kosherize output of format_title under these conditions */
      if (n_ref_format == REFTEIX5) {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "persName", "type", "author", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  return 801;
	}
      }
      else if (n_ref_format == REFRTF) {
	if (title_as_author == 1) {
	  if (print_rtf_format(ptr_ref, ptr_ref_len, "AUTHORLISTALTERNATE", dbires_ref, 1 /* start group */) == NULL) {
	    return 801;
	  }
	}
	else {
	  if (print_rtf_format(ptr_ref, ptr_ref_len, "EDITORLISTALTERNATE", dbires_ref, 1 /* start group */) == NULL) {
	    return 801;
	  }
	}
      }
      else {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "bibliomset", "relation", "author", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  return 801;
	}
      }

      /* todo: distinguish between title and periodical name */
      if (!strcmp(alternatetext, "JOURNALNAME")) {
	if ((new_ref = format_journalname(ptr_ref, ptr_ref_len, ptr_bibconns, dbires, dbires_ref, database, n_intext, ns, ptr_indent, n_ref_format, 1 /* title_as_author */)) == NULL) {
	  free(alist_preceeding);
	  clean_request(dbires_author);
	  return 801;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      else {
	if ((new_ref = format_title(ptr_ref, ptr_ref_len, ptr_bibconns, dbires, dbires_ref, title_as_author, n_intext, 1 /* title as author */, ns, ptr_indent, n_ref_format)) == NULL) {
	  free(alist_preceeding);
	  clean_request(dbires_author);
	  return 801;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      
      if (n_ref_format == REFTEIX5) {
	if (print_elend_x(ptr_ref, ptr_ref_len, "persName", ptr_indent, ns) == NULL) {
	  return 801;
	}
      }
      else if (n_ref_format == REFRTF) {
	if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
	  return 801;
	}
      }
      else {
	if (print_elend_x(ptr_ref, ptr_ref_len, "bibliomset", ptr_indent, ns) == NULL) {
	  return 801;
	}
      }

      nhave_content = 1;
      if (must_free_dbires) {
	dbi_result_free(dbires);
      }
    }
    else { /* regular author/editor */
      while (get_author_parts(dbires_author, &ainfo) != NULL) {
	/* increment counter, so first author will be 1 */
	nauthor_count++;
	nhave_content = 1;

	/* we may need different formats for the first, the last, and the inbetween authors */
	if (nauthor_count == 1) {
	  authorsep = NULL;
	  nameorder = dbi_result_get_string_idx(dbires_ref, n_namefirstorder_index);
	  initialstyle = dbi_result_get_string_idx(dbires_ref, n_namefirstinitial_index);
	  author_upper = dbi_result_get_string_idx(dbires_ref, n_namefirstupper_index);
	}
	else if (nauthor_count == num_authors) {
	  if (num_authors > 2) {
	    authorsep = my_dbi_result_get_string_idx(dbires_ref, n_threesepslast_index);
	  }
	  else {
	    authorsep = my_dbi_result_get_string_idx(dbires_ref, n_twoseps_index);
	  }
	  nameorder = dbi_result_get_string_idx(dbires_ref, n_nameotherorder_index);
	  initialstyle = dbi_result_get_string_idx(dbires_ref, n_nameotherinitial_index);
	  author_upper = dbi_result_get_string_idx(dbires_ref, n_nameotherupper_index);
	}
	else {
	  authorsep = my_dbi_result_get_string_idx(dbires_ref, n_threesepseach_index);
	  nameorder = dbi_result_get_string_idx(dbires_ref, n_nameotherorder_index);
	  initialstyle = dbi_result_get_string_idx(dbires_ref, n_nameotherinitial_index);
	  author_upper = dbi_result_get_string_idx(dbires_ref, n_nameotherupper_index);
	}

	/* if we don't want to display all authors, we'll display something like 'et.al' instead and exit the loop */
	if (nauthor_count > nauthor_display && num_authors > nauthor_max) {
	  if ((!n_intext && ptr_biblio_info->is_subseq) || (n_intext == 2)) {
	    new_ref = *ptr_ref;
	    item = my_dbi_result_get_string_copy_idx(dbires_ref, n_abbreviate_subseq_index);
	    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	      if (n_ref_format == REFTEIX5) {
		if (print_elstart_x(ptr_ref, ptr_ref_len, "seg", "type", "etal", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
		  clean_request(dbires_author);
		  free(item);
		  if (author_following) {
		    free(author_following);
		  }
		  return 801;
		}
	      }
	      else if (n_ref_format == REFRTF) {
		if (type == 1) {
		  if (print_rtf_format(ptr_ref, ptr_ref_len, "AUTHORLIST", dbires_ref, 1 /* start group */) == NULL) {
		    return 801;
		  }
		}
		else if (type == 2) {
		  if (print_rtf_format(ptr_ref, ptr_ref_len, "EDITORLIST", dbires_ref, 1 /* start group */) == NULL) {
		    return 801;
		  }
		}
		else if (type == 3) {
		  if (print_rtf_format(ptr_ref, ptr_ref_len, "SEDITORLIST", dbires_ref, 1 /* start group */) == NULL) {
		    return 801;
		  }
		}
		else {
		  if (print_rtf_format(ptr_ref, ptr_ref_len, "ALLALIST", dbires_ref, 1 /* start group */) == NULL) {
		    return 801;
		  }
		}
	      }
	      else {
		if (print_elstart_x(ptr_ref, ptr_ref_len, "bibliomset", "relation", "etal", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
		  clean_request(dbires_author);
		  free(item);
		  if (author_following) {
		    free(author_following);
		  }
		  return 801;
		}
	      }

	      if (sgml_entitize(&item, n_ref_format) == NULL) {
		clean_request(dbires_author);
		free(item);
		if (author_following) {
		  free(author_following);
		}
		return 801;
	      }
      
	      if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
		clean_request(dbires_author);
		free(item);
		if (author_following) {
		  free(author_following);
		}
		return 801;
	      }
	      else {
		*ptr_ref = new_ref;
	      }

	      if (n_ref_format == REFTEIX5) {
		if (print_elend_x(ptr_ref, ptr_ref_len, "seg", ptr_indent, ns) == NULL) {
		  clean_request(dbires_author);
		  if (author_following) {
		    free(author_following);
		  }
		  free(item);
		  return 801;
		}
	      }
	      else if (n_ref_format == REFRTF) {
		if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
		  return 801;
		}
	      }
	      else {
		if (print_elend_x(ptr_ref, ptr_ref_len, "bibliomset", ptr_indent, ns) == NULL) {
		  clean_request(dbires_author);
		  if (author_following) {
		    free(author_following);
		  }
		  free(item);
		  return 801;
		}
	      }
	    }
	    if (item) {
	      free(item);
	    }
	  }
	  else { /* not subseq */
	    new_ref = *ptr_ref;
	    item = my_dbi_result_get_string_copy_idx(dbires_ref, n_abbreviate_first_index);
	    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	      if (n_ref_format == REFTEIX5) {
		if (print_elstart_x(ptr_ref, ptr_ref_len, "seg", "type", "etal", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
		  clean_request(dbires_author);
		  free(item);
		  if (author_following) {
		    free(author_following);
		  }
		  return 801;
		}
	      }
	      else if (n_ref_format == REFRTF) {
		if (type == 1) {
		  if (print_rtf_format(ptr_ref, ptr_ref_len, "AUTHORLIST", dbires_ref, 1 /* start group */) == NULL) {
		    return 801;
		  }
		}
		else if (type == 2) {
		  if (print_rtf_format(ptr_ref, ptr_ref_len, "EDITORLIST", dbires_ref, 1 /* start group */) == NULL) {
		    return 801;
		  }
		}
		else if (type == 3) {
		  if (print_rtf_format(ptr_ref, ptr_ref_len, "SEDITORLIST", dbires_ref, 1 /* start group */) == NULL) {
		    return 801;
		  }
		}
		else {
		  if (print_rtf_format(ptr_ref, ptr_ref_len, "ALLALIST", dbires_ref, 1 /* start group */) == NULL) {
		    return 801;
		  }
		}
	      }
	      else {
		if (print_elstart_x(ptr_ref, ptr_ref_len, "bibliomset", "relation", "etal", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
		  clean_request(dbires_author);
		  free(item);
		  if (author_following) {
		    free(author_following);
		  }
		  return 801;
		}
	      }

	      if (sgml_entitize(&item, n_ref_format) == NULL) {
		clean_request(dbires_author);
		free(item);
		if (author_following) {
		  free(author_following);
		}
		return 801;
	      }
      
	      if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
		clean_request(dbires_author);
		free(item);
		if (author_following) {
		  free(author_following);
		}
		return 801;
	      }
	      else {
		*ptr_ref = new_ref;
	      }

	      if (n_ref_format == REFTEIX5) {
		if (print_elend_x(ptr_ref, ptr_ref_len, "seg", ptr_indent, ns) == NULL) {
		  clean_request(dbires_author);
		  if (author_following) {
		    free(author_following);
		  }
		  return 801;
		}
	      }
	      else if (n_ref_format == REFRTF) {
		if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
		  return 801;
		}
	      }
	      else {
		if (print_elend_x(ptr_ref, ptr_ref_len, "bibliomset", ptr_indent, ns) == NULL) {
		  clean_request(dbires_author);
		  if (author_following) {
		    free(author_following);
		  }
		  return 801;
		}
	      }
	    }
	    if (item) {
	      free(item);
	    }
	  }
	  *ptr_ref = new_ref;
	  break;
	}

	if ((n_status = format_authorname(ptr_ref, ptr_ref_len, &ainfo, authorsep, nameorder, initialstyle, author_upper, n_intext, type, ns, ptr_indent, n_ref_format, &n_trailing_dot)) != 0) {
	  clean_request(dbires_author);
	  if (author_following) {
	    free(author_following);
	  }
	  return n_status;
	}

/* 	*ptr_ref = new_ref; */
      } /* end while have authors */
    } /* end if have author_anonymous */

    clean_request(dbires_author);
  } /* end if author query ok */

  /* text following */
  if (author_following && *author_following) { /* following */
    if (sgml_entitize(&author_following, n_ref_format) == NULL) {
      free(author_following);
      return 801;
    }
    
    if (n_trailing_dot
	&& *author_following == '.') {
      if ((new_ref = mstrcat(*ptr_ref, author_following+1, ptr_ref_len, 0)) == NULL) {
	free(author_following);
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    else {
      if ((new_ref = mstrcat(*ptr_ref, author_following, ptr_ref_len, 0)) == NULL) {
	free(author_following);
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
    }

    if (author_following[strlen(author_following)-1] == '.') {
      n_trailing_dot++;
    }
  }
  if (author_following) {
    free(author_following);
  }

  item = my_dbi_result_get_string_copy_idx(dbires_ref, n_following_index);
  if (item && *item) { /* following */
    if (sgml_entitize(&item, n_ref_format) == NULL) {
      free(item);
      return 801;
    }
      
    /* skip period if the last author ended with a period */
    if (*item == '.' && n_trailing_dot) {
      /* skip period */
      if ((new_ref = mstrcat(*ptr_ref, item +1, ptr_ref_len, 0)) == NULL) {
	free(item);
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    else {
      if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	free(item);
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
  }
  if (item) {
    free(item);
  }
/*   printf("end format_authorlist():%s\n", *ptr_ref); */

  if (!nhave_content) {
    (*ptr_ref)[0] = '\0'; /* return empty string if no real content */
  }
  return 0;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_pubdate(): formats the pubdate/pubdatesec part of a
                    bibliography entry as a DocBook bibliomixed text

  char* format_pubdate returns ptr to the buffer if successful, NULL if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least 4096 byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  struct BIBCONNS* ptr_bibconns ptr to structure with database connections

  dbi_result dbires ptr to a dbi result structure containing the
                    current reference

  dbi_result dbires_ref ptr to a dbi result structure containing the
                    reference style

  dbi_result dbires_cit ptr to a dbi result structure containing the
                        current citation style info

  int type 0 = pubdate, 1 = pubdatesec, 2 = pubdateall

  int n_intext  0 = bibliography citation; 1 = intext citation; 2 =
                    intext citation subsequent

  char* year_unique_suffix ptr to a string with a unique suffix for 
                   pubyear

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* format_pubdate(char** ptr_ref, size_t* ptr_ref_len, struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_ref, dbi_result dbires_cit, int type, int n_intext, const char* year_unique_suffix, const char* ns, struct xmlindent* ptr_indent, int n_ref_format) {
  char* new_ref;
  char pubdaterole[28];
  char year_buffer[32];
  char dummy_year[] = "nd";
  char *item;
  char *item_dateother;
  char *item_firstsep;
  char *item_secondsep;
  const char *citem;
  const char *item_yearformat;
  const char *item_monthformat;
  const char *item_dayformat;
  const char *item_pad;
  const char *item_sequence;
  const char *item_format;
  const char default_sequence[] = "Y"; /* defaults are taken from citestylex.dtd. Be aware of code duplication which is bad per se */
  const char default_yearformat[] = "FOURDIGIT";
  const char default_pad[] = "NN";
  const char default_monthformat[] = "ARABICMONTH";
  const char default_dayformat[] = "ARABICDAY";
  enum refdbref_col n_date_index;
  enum refdbref_col n_dateother_index;
  int nhave_content = 0;
  unsigned int n_pubyear = 0;
  enum refdb_col n_preceeding_index;
  enum refdb_col n_following_index;
  enum refdb_col n_format_index;
  enum refdb_col n_sequence_index;
  enum refdb_col n_firstsep_index;
  enum refdb_col n_secondsep_index;
  enum refdb_col n_yearformat_index;
  enum refdb_col n_monthformat_index;
  enum refdb_col n_dayformat_index;
  enum refdb_col n_pad_index;
/*   dbi_conn conn; */
  struct lilimem sentinel;

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

  /* set indices according to pubdate type */
  if (type == 1) {
    n_date_index = REFDB_SECYEAR;
    n_dateother_index = REFDB_SECOTHER_INFO;
    n_preceeding_index = PUBDATESECPRECEEDING;
    n_following_index = PUBDATESECFOLLOWING;
    n_format_index = PUBDATESECFORMAT;
    n_sequence_index = PUBDATESECSEQUENCE;
    n_firstsep_index = PUBDATESECFIRSTSEP;
    n_secondsep_index = PUBDATESECSECONDSEP;
    n_yearformat_index = PUBDATESECYEARFORMAT;
    n_monthformat_index = PUBDATESECMONTHFORMAT;
    n_dayformat_index = PUBDATESECDAYFORMAT;
    n_pad_index = PUBDATESECPADLEADINGZERO;
    strcpy(pubdaterole, "secondary");
  }
  else if (type == 2) {
    n_preceeding_index = PUBDATEALLPRECEEDING;
    n_following_index = PUBDATEALLFOLLOWING;
    n_format_index = PUBDATEALLFORMAT;
    n_sequence_index = PUBDATEALLSEQUENCE;
    n_firstsep_index = PUBDATEALLFIRSTSEP;
    n_secondsep_index = PUBDATEALLSECONDSEP;
    n_yearformat_index = PUBDATEALLYEARFORMAT;
    n_monthformat_index = PUBDATEALLMONTHFORMAT;
    n_dayformat_index = PUBDATEALLDAYFORMAT;
    n_pad_index = PUBDATEALLPADLEADINGZERO;
    strcpy(pubdaterole, "all");
  }
  else { /* 0 */
    n_date_index = REFDB_PUBYEAR;
    n_dateother_index = REFDB_PYOTHER_INFO;
    n_preceeding_index = PUBDATEPRECEEDING;
    n_following_index = PUBDATEFOLLOWING;
    n_format_index = PUBDATEFORMAT;
    n_sequence_index = PUBDATESEQUENCE;
    n_firstsep_index = PUBDATEFIRSTSEP;
    n_secondsep_index = PUBDATESECONDSEP;
    n_yearformat_index = PUBDATEYEARFORMAT;
    n_monthformat_index = PUBDATEMONTHFORMAT;
    n_dayformat_index = PUBDATEDAYFORMAT;
    n_pad_index = PUBDATEPADLEADINGZERO;
    strcpy(pubdaterole, "primary");
  }

/*    printf("in format_pubdate. year_unique_suffix=%s; type=%d, n_intext=%d<<\n", year_unique_suffix, type, n_intext); */

/*   conn = dbi_result_get_conn(dbires); */

  if (type == 2) {
    /* first try pubdate, then pubdatesec */
    n_pubyear = dbi_result_get_ushort_idx(dbires, REFDB_PUBYEAR);
    if (!n_pubyear/*  && my_dbi_conn_error_flag(ptr_bibconns->conn_source) */) { /* reference has no pubdate data */
      citem = my_dbi_result_get_string_idx(dbires, REFDB_PYOTHER_INFO);
      if (!citem || !*citem) { /* reference has no pubdate data */
	return *ptr_ref;
      }
      else {
	n_dateother_index = REFDB_SECOTHER_INFO;
      }
      n_pubyear = dbi_result_get_ushort_idx(dbires, REFDB_SECYEAR);
    }
    else {
      n_date_index = REFDB_PUBYEAR;
      n_dateother_index = REFDB_PYOTHER_INFO;
    }
  }
  else {
    /* use only requested date */
    n_pubyear = dbi_result_get_ushort_idx(dbires, n_date_index);
    if (!n_pubyear/*  && my_dbi_conn_error_flag(ptr_bibconns->conn_source) */) { /* reference has no pubdate data */
      return *ptr_ref;
    }
  }

  item = my_dbi_result_get_string_copy_idx(dbires_ref, n_preceeding_index);
  if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
    if (sgml_entitize(&item, n_ref_format) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(item);
      return NULL;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  if (item) {
    free(item);
  }

  if (n_ref_format == REFTEIX5) {
    if (print_elstart_x(ptr_ref, ptr_ref_len, "date", "rend", pubdaterole, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return NULL;
    }
  }
  else if (n_ref_format == REFRTF) {
    if (print_rtf_format(ptr_ref, ptr_ref_len, "PUBDATE", dbires_ref, 1 /* start group */) == NULL) {
      return NULL;
    }
  }
  else {
    if (print_elstart_x(ptr_ref, ptr_ref_len, "pubdate", "role", pubdaterole, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return NULL;
    }
  }

  item_dateother = my_dbi_result_get_string_copy_idx(dbires, n_dateother_index);
  
  if (item_dateother && *item_dateother && !my_dbi_conn_error_flag(ptr_bibconns->conn_source)) {
    if (sgml_entitize(&item_dateother, n_ref_format) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(item_dateother);
      return NULL;
    }
    else {
      if (insert_lilimem(&sentinel, (void**)&item_dateother, "item_dateother")) {
	delete_all_lilimem(&sentinel);
	return NULL;
      }
    }
  }
  else if (item_dateother && *item_dateother) {
    free(item_dateother);
    item_dateother = NULL;
  }

  item_format = dbi_result_get_string_idx(dbires_ref, n_format_index);

  /* switch on formatting if the attribute is either missing or set to "YES". To disable formatting, the attribute has to be set to "NO" explicitly */
  if (!item_format || strcmp(item_format, "YES") == 0) {
    /* do some formatting here */
    item_sequence = dbi_result_get_string_idx(dbires_ref, n_sequence_index);
    if (!item_sequence) {
      item_sequence = default_sequence;
    }

    item_yearformat = dbi_result_get_string_idx(dbires_ref, n_yearformat_index);
    if (!item_yearformat) {
      item_yearformat = default_yearformat;
    }

    item_pad = dbi_result_get_string_idx(dbires_ref, n_pad_index);
    if (!item_pad) {
      item_pad = default_pad;
    }

    item_monthformat = dbi_result_get_string_idx(dbires_ref, n_monthformat_index);
    if (!item_monthformat) {
      item_monthformat = default_monthformat;
    }

    item_dayformat = dbi_result_get_string_idx(dbires_ref, n_dayformat_index);
    if (!item_dayformat) {
      item_dayformat = default_dayformat;
    }

    item_firstsep = my_dbi_result_get_string_copy_idx(dbires_ref, n_firstsep_index);
    if (item_firstsep && *item_firstsep && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
      if (sgml_entitize(&item_firstsep, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item_firstsep);
	return NULL;
      }
      else {
	if (insert_lilimem(&sentinel, (void**)&item_firstsep, "item_firstsep")) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      }
    }
    else if (item_firstsep) {
      free(item_firstsep);
      item_firstsep = NULL;
    }

    item_secondsep = my_dbi_result_get_string_copy_idx(dbires_ref, n_secondsep_index);
    if (item_secondsep && *item_secondsep && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
      if (sgml_entitize(&item_secondsep, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item_secondsep);
	return NULL;
      }
      else {
	if (insert_lilimem(&sentinel, (void**)&item_secondsep, "item_secondsep")) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      }
    }
    else if (item_secondsep) {
      free(item_secondsep);
      item_secondsep = NULL;
    }

/*     printf("item_sequence went to %s<<\n", item_sequence); */
    if (item_sequence && strcmp(item_sequence, "Y") == 0) {
      if (n_pubyear) {
	nhave_content = 1;
	if ((new_ref = format_year(*ptr_ref, ptr_ref_len, n_pubyear, item_yearformat, item_pad, year_unique_suffix)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      else {
	/* insert dummy pubdate */
	/* we do not increment nhave_content here. If the missing pubdate is
	   all we "have", the whole element should be omitted */
	if ((new_ref = mstrcat(*ptr_ref, dummy_year, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
    }
    else if (item_sequence && strcmp(item_sequence, "DM") == 0) {
      if (item_dateother && *item_dateother) {
	nhave_content = 1;
	if ((new_ref = format_day(*ptr_ref, ptr_ref_len, item_dateother, item_dayformat, item_pad)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}

	/* firstsep goes in here */
	if (item_firstsep && *item_firstsep) {
	  if ((new_ref = mstrcat(*ptr_ref, item_firstsep, ptr_ref_len, 0)) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    delete_all_lilimem(&sentinel);
	    return NULL;
	  }
	  else {
	    *ptr_ref = new_ref;
	  }
	}

	if ((new_ref = format_month(*ptr_ref, ptr_ref_len, item_dateother, item_monthformat, item_pad, dbires_cit)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
    }
    else if (item_sequence && strcmp(item_sequence, "MD") == 0) {
      if (item_dateother && *item_dateother) {
	nhave_content = 1;
	if ((new_ref = format_month(*ptr_ref, ptr_ref_len, item_dateother, item_monthformat, item_pad, dbires_cit)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}

	/* firstsep goes in here */
	if (item_firstsep && *item_firstsep) {
	  if ((new_ref = mstrcat(*ptr_ref, item_firstsep, ptr_ref_len, 0)) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    delete_all_lilimem(&sentinel);
	    return NULL;
	  }
	  else {
	    *ptr_ref = new_ref;
	  }
	}

	if ((new_ref = format_day(*ptr_ref, ptr_ref_len, item_dateother, item_dayformat, item_pad)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
    }
    else if (item_sequence && strcmp(item_sequence, "MY") == 0) {
      if (item_dateother && *item_dateother) {
	nhave_content = 1;
	if ((new_ref = format_month(*ptr_ref, ptr_ref_len, item_dateother, item_monthformat, item_pad, dbires_cit)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      /* firstsep goes in here */
      if (item_firstsep && *item_firstsep) {
	if ((new_ref = mstrcat(*ptr_ref, item_firstsep, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      if (n_pubyear) {
	nhave_content = 1;

	if ((new_ref = format_year(*ptr_ref, ptr_ref_len, n_pubyear, item_yearformat, item_pad, year_unique_suffix)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      else {
	/* insert dummy pubdate */
	if ((new_ref = mstrcat(*ptr_ref, dummy_year, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
    }
    else if (item_sequence && strcmp(item_sequence, "DMY") == 0) {
      if (item_dateother && *item_dateother) {
	nhave_content = 1;
	if ((new_ref = format_day(*ptr_ref, ptr_ref_len, item_dateother, item_dayformat, item_pad)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      /* firstsep goes in here */
      if (item_firstsep && *item_firstsep) {
	if ((new_ref = mstrcat(*ptr_ref, item_firstsep, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      if (item_dateother && *item_dateother) {
	if ((new_ref = format_month(*ptr_ref, ptr_ref_len, item_dateother, item_monthformat, item_pad, dbires_cit)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      /* secondsep goes in here */
      if (item_secondsep && *item_secondsep) {
	if ((new_ref = mstrcat(*ptr_ref, item_secondsep, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      if (n_pubyear) {
	if ((new_ref = format_year(*ptr_ref, ptr_ref_len, n_pubyear, item_yearformat, item_pad, year_unique_suffix)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      else {
	/* insert dummy pubdate */
	if ((new_ref = mstrcat(*ptr_ref, dummy_year, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
    }
    else if (item_sequence && strcmp(item_sequence, "MDY") == 0) {
      if (item_dateother && *item_dateother) {
	nhave_content = 1;

	if ((new_ref = format_month(*ptr_ref, ptr_ref_len, item_dateother, item_monthformat, item_pad, dbires_cit)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      /* firstsep goes in here */
      if (item_firstsep && *item_firstsep) {
	if ((new_ref = mstrcat(*ptr_ref, item_firstsep, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      if (item_dateother && *item_dateother) {
	if ((new_ref = format_day(*ptr_ref, ptr_ref_len, item_dateother, item_dayformat, item_pad)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      /* secondsep goes in here */
      if (item_secondsep && *item_secondsep) {
	if ((new_ref = mstrcat(*ptr_ref, item_secondsep, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      if (n_pubyear) {
	if ((new_ref = format_year(*ptr_ref, ptr_ref_len, n_pubyear, item_yearformat, item_pad, year_unique_suffix)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      else {
	/* insert dummy pubdate */
	if ((new_ref = mstrcat(*ptr_ref, dummy_year, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
    }
    else if (item_sequence && strcmp(item_sequence, "YMD") == 0) {
      if (n_pubyear) {
	if ((new_ref = format_year(*ptr_ref, ptr_ref_len, n_pubyear, item_yearformat, item_pad, year_unique_suffix)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      else {
	/* insert dummy pubdate */
	if ((new_ref = mstrcat(*ptr_ref, dummy_year, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      /* firstsep goes in here */
      if (item_firstsep && *item_firstsep) {
	if ((new_ref = mstrcat(*ptr_ref, item_firstsep, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      if (item_dateother && *item_dateother) {
	nhave_content = 1;

	if ((new_ref = format_month(*ptr_ref, ptr_ref_len, item_dateother, item_monthformat, item_pad, dbires_cit)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      /* secondsep goes in here */
      if (item_secondsep && *item_secondsep) {
	if ((new_ref = mstrcat(*ptr_ref, item_secondsep, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      if (item_dateother && *item_dateother) {
	if ((new_ref = format_day(*ptr_ref, ptr_ref_len, item_dateother, item_dayformat, item_pad)) == NULL) {
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
    }
  }
  else if (n_pubyear) {
    /* no formatting */
    nhave_content = 1;

    sprintf(year_buffer, "%d", n_pubyear);
    if ((new_ref = mstrcat(*ptr_ref, year_buffer, ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      delete_all_lilimem(&sentinel);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  else {
    /* insert dummy pubdate */
    if ((new_ref = mstrcat(*ptr_ref, dummy_year, ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      delete_all_lilimem(&sentinel);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
  }

  delete_all_lilimem(&sentinel);

  if (n_ref_format == REFTEIX5) {
    if (print_elend_x(ptr_ref, ptr_ref_len, "date", ptr_indent, ns) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return NULL;
    }
  }
  else if (n_ref_format == REFRTF) {
    if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
      return NULL;
    }
  }
  else {
    if (print_elend_x(ptr_ref, ptr_ref_len, "pubdate", ptr_indent, ns) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return NULL;
    }
  }

  item = my_dbi_result_get_string_copy_idx(dbires_ref, n_following_index);
  if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* following */
    if (sgml_entitize(&item, n_ref_format) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(item);
      return NULL;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(item);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  if (item) {
    free(item);
  }

/*    printf("done with format_pubdate. ptr_ref=%s<<\n", *ptr_ref); */
  if (!nhave_content) {
    (*ptr_ref)[0] ='\0';
  }
  return *ptr_ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_title(): formats the title/booktitle/seriestitle part of a
                      bibliography entry as a DocBook bibliomixed text

  char* format_title returns ptr to the buffer if successful, NULL if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least 4096 byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  dbi_result dbires ptr to a dbi result structure containing the
                    current reference

  dbi_result dbires_ref ptr to a dbi result structure containing the
                        reference style

  int type 1=title (part), 2=booktitle (pub), 3=seriestitle (series),
           4=alltitle (any)

  int n_intext  0 = bibliography citation; 1 = intext citation; 2 =
                    intext citation subsequent

  int n_title_as_author if >0, title is printed instead of authors

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* format_title(char** ptr_ref, size_t* ptr_ref_len, struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_ref, int type, int n_intext, int n_title_as_author, const char* ns, struct xmlindent* ptr_indent, int n_ref_format) {
  char* new_ref;
  char role[7];
  char roleatt[6] = "role";
  char *item;
  char *real_item_title = NULL;
  const char *citem;
  const char *item_title;
  const char *item_titlebook;
  const char *item_titleseries;
  int n_title_index = 0;
  int nhave_content = 0;
  int nhave_punctuation = 0;
  enum refdb_col n_preceeding_index;
  enum refdb_col n_following_index;
  int n_uplow = 0; /* 0=asis, 1=uppercase, 2=lowercase, 3=initialcaps */

/*    printf("start format_title. title_as_author=%d\n", n_title_as_author); */

  /* set a few parameters based on title type */
  switch (type) {
  case 1:
    n_title_index = REFDB_TITLE;
    n_preceeding_index = TITLEPRECEEDING;
    n_following_index = TITLEFOLLOWING;
    citem = dbi_result_get_string_idx(dbires_ref, TITLECASE);
    if (citem && strcmp(citem, "UPPER") == 0) {
      n_uplow = 1;
    }
    else if (citem && strcmp(citem, "LOWER") == 0) {
      n_uplow = 2;
    }
    else if (citem && strcmp(citem, "ICAPS") == 0) {
      n_uplow = 3;
    }
    if (n_ref_format == REFTEIX5) {
      strcpy(role, "a");
      strcpy(roleatt, "level");
    }
    else {
      strcpy(role, "JOUR");
    }
    break;
  default: /* same as booktitle, fall through */
  case 2:
    n_title_index = REFDB_BOOKTITLE;
    n_preceeding_index = BOOKTITLEPRECEEDING;
    n_following_index = BOOKTITLEFOLLOWING;
    citem = dbi_result_get_string_idx(dbires_ref, BOOKTITLECASE);
    if (citem && strcmp(citem, "UPPER") == 0) {
      n_uplow = 1;
    }
    else if (citem && strcmp(citem, "LOWER") == 0) {
      n_uplow = 2;
    }
    else if (citem && strcmp(citem, "ICAPS") == 0) {
      n_uplow = 3;
    }
    if (n_ref_format == REFTEIX5) {
      strcpy(role, "m");
      strcpy(roleatt, "level");
    }
    else {
      strcpy(role, "BOOK");
    }
    break;
  case 3:
    n_title_index = REFDB_TITLE_SERIES;
    n_preceeding_index = SERIESTITLEPRECEEDING;
    n_following_index = SERIESTITLEFOLLOWING;
    citem = dbi_result_get_string_idx(dbires_ref, SERIESTITLECASE);
    if (citem && strcmp(citem, "UPPER") == 0) {
      n_uplow = 1;
    }
    else if (citem && strcmp(citem, "LOWER") == 0) {
      n_uplow = 2;
    }
    else if (citem && strcmp(citem, "ICAPS") == 0) {
      n_uplow = 3;
    }
    if (n_ref_format == REFTEIX5) {
      strcpy(role, "s");
      strcpy(roleatt, "level");
    }
    else {
      strcpy(role, "SERIES");
    }
    break;
  case 4:
    item_title = my_dbi_result_get_string_idx(dbires, REFDB_TITLE);
    if (item_title && my_dbi_conn_error_flag(ptr_bibconns->conn_source)) {
      item_title = NULL;
    }
    item_titlebook = my_dbi_result_get_string_idx(dbires, REFDB_BOOKTITLE);
    if (item_titlebook && my_dbi_conn_error_flag(ptr_bibconns->conn_source)) {
      item_titlebook = NULL;
    }
    item_titleseries = my_dbi_result_get_string_idx(dbires, REFDB_TITLE_SERIES);
    if (item_titleseries && my_dbi_conn_error_flag(ptr_bibconns->conn_source)) {
      item_titleseries = NULL;
    }

    if (item_title && *item_title) {
      n_title_index = REFDB_TITLE;
    }
    else if (item_titlebook && *item_titlebook) {
      n_title_index = REFDB_BOOKTITLE;
    }
    else if (item_titleseries && *item_titleseries) {
      n_title_index = REFDB_TITLE_SERIES;
    }
    n_preceeding_index = ALLTITLEPRECEEDING;
    n_following_index = ALLTITLEFOLLOWING;
    citem = dbi_result_get_string_idx(dbires_ref, ALLTITLECASE);
    if (citem && strcmp(citem, "UPPER") == 0) {
      n_uplow = 1;
    }
    else if (citem && strcmp(citem, "LOWER") == 0) {
      n_uplow = 2;
    }
    else if (citem && strcmp(citem, "ICAPS") == 0) {
      n_uplow = 3;
    }
    if (n_ref_format == REFTEIX5) {
      strcpy(roleatt, "level");
    }
    strcpy(role, "ALL");
    break;
  }

  if (!n_title_index) { /* no title data available */
    goto Finish; 
  }

  /* now assemble the string */
  real_item_title = my_dbi_result_get_string_copy_idx(dbires, n_title_index);
  if (real_item_title && *real_item_title && !my_dbi_conn_error_flag(ptr_bibconns->conn_source)) { /* reference has title data */
    nhave_content = 1;
    item = my_dbi_result_get_string_copy_idx(dbires_ref, n_preceeding_index);
    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
      if (sgml_entitize(&item, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	free(real_item_title);
	return NULL;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	free(real_item_title);
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    if (item) {
      free(item);
    }

    /* all types go into a title element. */
    if (n_title_as_author) {
      if (n_ref_format == REFRTF) {
	/* todo: formatting should have already been set by the author code */
/* 	if (type == 1) { */
/* 	  if (print_rtf_format(ptr_ref, ptr_ref_len, "TITLE", dbires_ref, 1 /\* start group *\/) == NULL) { */
/* 	    return NULL; */
/* 	  } */
/* 	} */
/* 	else if (type == 2) { */
/* 	  if (print_rtf_format(ptr_ref, ptr_ref_len, "BOOKTITLE", dbires_ref, 1 /\* start group *\/) == NULL) { */
/* 	    return NULL; */
/* 	  } */
/* 	} */
/* 	else if (type == 3) { */
/* 	  if (print_rtf_format(ptr_ref, ptr_ref_len, "SERIESTITLE", dbires_ref, 1 /\* start group *\/) == NULL) { */
/* 	    return NULL; */
/* 	  } */
/* 	} */
/* 	else { */
/* 	  if (print_rtf_format(ptr_ref, ptr_ref_len, "ALLTITLE", dbires_ref, 1 /\* start group *\/) == NULL) { */
/* 	    return NULL; */
/* 	  } */
/* 	} */
      }
      else {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "title", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(real_item_title);
	  return NULL;
	}
      }
    }
    else {
      if (n_ref_format == REFRTF) {
	if (type == 1) {
	  if (print_rtf_format(ptr_ref, ptr_ref_len, "TITLE", dbires_ref, 1 /* start group */) == NULL) {
	    return NULL;
	  }
	}
	else if (type == 2) {
	  if (print_rtf_format(ptr_ref, ptr_ref_len, "BOOKTITLE", dbires_ref, 1 /* start group */) == NULL) {
	    return NULL;
	  }
	}
	else if (type == 3) {
	  if (print_rtf_format(ptr_ref, ptr_ref_len, "SERIESTITLE", dbires_ref, 1 /* start group */) == NULL) {
	    return NULL;
	  }
	}
	else {
	  if (print_rtf_format(ptr_ref, ptr_ref_len, "ALLTITLE", dbires_ref, 1 /* start group */) == NULL) {
	    return NULL;
	  }
	}
      }
      else {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "title", roleatt, role, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(real_item_title);
	  return NULL;
	}
      }
    }

    if (n_uplow == 1) { /* want uppercase */
      strup(real_item_title);
    }
    else if (n_uplow == 2) { /* want lowercase */
      strdn(real_item_title);
    }
    else if (n_uplow == 3) { /* initial caps */
      stricap(real_item_title);
    }
    /* else: leave as is */

    if (sgml_entitize(&real_item_title, n_ref_format) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(real_item_title);
      return NULL;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, real_item_title, ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(real_item_title);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }

    /* check for trailing punctuation */
    if (real_item_title[strlen(real_item_title) - 1] == '?'
	|| real_item_title[strlen(real_item_title) - 1] == '!'
	|| real_item_title[strlen(real_item_title) - 1] == '.') {
      nhave_punctuation++;
    }

    free(real_item_title);
    real_item_title = NULL;

    if (n_ref_format == REFRTF) {
      if (!n_title_as_author) {
	if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
	  return NULL;
	}
      }
    }
    else {
      if (print_elend_x(ptr_ref, ptr_ref_len, "title", ptr_indent, ns) == NULL) {
	
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
      }
    }
 
    item = my_dbi_result_get_string_copy_idx(dbires_ref, n_following_index);
    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* following */
      if (sgml_entitize(&item, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	return NULL;
      }
      
      /* skip period if title ended with some sort of punctuation */
      if (nhave_punctuation && *item == '.') {
	if ((new_ref = mstrcat(*ptr_ref, item+1, ptr_ref_len, 0)) == NULL) {
	  free(item);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      else {
	if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	  free(item);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
    }
    if (item) {
      free(item);
    }
  }
    /* else: no title found */
/*   else { */
/*     printf("no title found\n"); */
/*   } */

  if (real_item_title) {
    free(real_item_title);
  }

/*    printf("end format_title:%s\n", *ref); */
 Finish:
  if (!nhave_content) {
    (*ptr_ref)[0] = '\0';
  }
  return *ptr_ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_ulink(): formats the url/link part of a
                      bibliography entry as a DocBook bibliomixed text

  char* format_ulink returns ptr to the buffer if successful, NULL if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least 4096 byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  dbi_result dbires ptr to a dbi result structure containing the
                    current reference

  dbi_result dbires_ref ptr to a dbi result structure containing the
                        reference style

  int type 0=url, 1=pdf, 2=fulltext, 3=related, 4=image

  int n_intext  0 = bibliography citation; 1 = intext citation; 2 =
                    intext citation subsequent

  int n_preceeding_index the index of the preceeding stuff in dbires_ref

  int n_following_index the index of the following stuff in dbires_ref

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  const char* pdfroot optional path prefix for file:// links

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* format_ulink(char** ptr_ref, size_t* ptr_ref_len, struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_ref, int type, int n_intext, int n_preceeding_index, int n_following_index, const char* ns, struct xmlindent* ptr_indent, int n_ref_format, const char* pdfroot) {
  char* new_ref;
  char role_string[16];
  char *preceeding;
  char *following;
  char *entitize_string;
  const char *citem;
  dbi_result dbires_ulink;

/*   printf("start format_ulink, preceeding:%d; following:%d\n", n_preceeding_index, n_following_index); */

  dbires_ulink = request_ulinks(ptr_bibconns->conn, my_dbi_result_get_idval(dbires, "refdb_id"), 0 /* ref entry */, type /* link type */, 0 /* is_temp */, NULL /* todo: use only user's own links? */);
  if (dbires_ulink == NULL) {
    LOG_PRINT(LOG_DEBUG, "requesting links failed");
    return NULL;
  }

  if (!dbi_result_get_numrows(dbires_ulink)) {
    /* return unaltered string */
    return *ptr_ref;
  }

  preceeding = my_dbi_result_get_string_copy_idx(dbires_ref, n_preceeding_index);

  if (preceeding && *preceeding && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
    if (sgml_entitize(&preceeding, n_ref_format) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(preceeding);
      return NULL;
    }
  }

  following = my_dbi_result_get_string_copy_idx(dbires_ref, n_following_index);
  if (following && *following && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* following */
    if (sgml_entitize(&following, n_ref_format) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(following);
      if (preceeding) {
	free(preceeding);
      }
      return NULL;
    }
  }
      

  if (n_ref_format == REFRTF) {
    char linktype[6];
    snprintf(linktype, 6, "LINK%d", type);

    if (print_rtf_format(ptr_ref, ptr_ref_len, linktype, dbires_ref, 1 /* start group */) == NULL) {
      return NULL;
    }
  }
  else if (n_ref_format != REFTEIX5) {
    if (print_elstart_x(ptr_ref, ptr_ref_len, "bibliomisc", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      return NULL;
    }
  }


  while ((citem = get_ulink(dbires_ulink)) != NULL) {
    if (preceeding) {
      if ((new_ref = mstrcat(*ptr_ref, preceeding, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(preceeding);
	if (following) {
	  free(following);
	}
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
    }

    if (type > 0 && type < 5) {
      entitize_string = add_root_to_link(citem, pdfroot);
    }
    else {
      entitize_string = strdup(citem);
    }

    if (entitize_string == NULL
	|| sgml_entitize(&entitize_string, n_ref_format) == NULL) {
      if (preceeding) {
	free(preceeding);
      }
      if (following) {
	free(following);
      }
      return NULL;
    }
    
    if (!type) {
      strncpy(role_string, "url", 16);
    }
    else if (type == 1) {
      strncpy(role_string, "pdf", 16);
    }
    else if (type == 2) {
      strncpy(role_string, "fulltext", 16);
    }
    else if (type == 3) {
      strncpy(role_string, "related", 16);
    }
    else if (type == 4) {
      strncpy(role_string, "image", 16);
    }

    if (n_ref_format == REFDOCBKX5) {
      if (print_element_x(entitize_string, ptr_ref, ptr_ref_len, "link", "xl:role", role_string, "xl:href", entitize_string, ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_CRIT, get_status_msg(801));
	if (preceeding) {
	  free(preceeding);
	}
	if (following) {
	  free(following);
	}
	return NULL;
      }
    }
    else if (n_ref_format == REFRTF) {
      /* todo: create hyperlink */
      if (print_rtf_string(entitize_string, ptr_ref, ptr_ref_len) == NULL) {
	LOG_PRINT(LOG_CRIT, get_status_msg(801));
	if (preceeding) {
	  free(preceeding);
	}
	if (following) {
	  free(following);
	}
	return NULL;
      }
    }
    else if (n_ref_format == REFTEIX5) {
      if (print_element_x(entitize_string, ptr_ref, ptr_ref_len, "seg", "type", "ulink", NULL, NULL, ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_CRIT, get_status_msg(801));
	if (preceeding) {
	  free(preceeding);
	}
	if (following) {
	  free(following);
	}
	return NULL;
      }
    }
    else {
      if (print_element_x(entitize_string, ptr_ref, ptr_ref_len, "ulink", "role", role_string, "url", entitize_string, ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_CRIT, get_status_msg(801));
	if (preceeding) {
	  free(preceeding);
	}
	if (following) {
	  free(following);
	}
	return NULL;
      }
    }

    free(entitize_string);

    if (following) {
      if ((new_ref = mstrcat(*ptr_ref, following, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(following);
	if (preceeding) {
	  free(preceeding);
	}
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
  } /* end while */

  clean_request(dbires_ulink);

  if (preceeding) {
    free(preceeding);
  }
  if (following) {
    free(following);
  }

  if (n_ref_format == REFRTF) {
    if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
      return NULL;
    }
  }
  else if (n_ref_format != REFTEIX5) {
    if (print_elend_x(ptr_ref, ptr_ref_len, "bibliomisc", ptr_indent, ns) == NULL) {
      LOG_PRINT(LOG_CRIT, get_status_msg(801));
      return NULL;
    }
  }

/*    printf("end format_ulink:%s\n", *ref); */

  return *ptr_ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_journalname(): formats the journalname part of a bibliography
                      entry as a DocBook bibliomixed text

  char* format_journalname returns ptr to the buffer if successful, NULL if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least 4096 byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  struct BIBCONNS* ptr_bibconns ptr to structure with database connections

  dbi_result dbires ptr to a dbi result structure containing the
                    current reference

  dbi_result dbires_ref ptr to a dbi result structure containing the
                        pubtype style

  const char* database ptr to a string containing the database name of the
                 current reference

  int n_intext  0 = bibliography citation; 1 = intext citation; 2 =
                    intext citation subsequent; 3 = bibtex output
		    
  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  int n_title_as_author if nonzero, journal name is used instead of authorname

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* format_journalname(char** ptr_ref, size_t* ptr_ref_len, struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_ref, const char* database, int n_intext, const char* ns, struct xmlindent* ptr_indent, int n_ref_format, int n_title_as_author) {
  int nhave_content = 0;
  int nhave_trailing_dot = 0;
  int errcode; /* receives error code for periodical requests */
  char *new_ref;
  char *period;
  char periodical[256] = "";
  char *item;
  const char *citem;
  char *mod_periodical;

  /* try the first choice of the periodical name */
  citem = dbi_result_get_string_idx(dbires_ref, JOURNALNAMEDEFAULTTEXT);
  if (citem && strcmp(citem, "ABBREV") == 0) {
    item = get_periodical(ptr_bibconns->conn_source, periodical, database, 3, &errcode, 0, my_dbi_result_get_idval(dbires, "refdb_id"), NULL /* no frequency required */);
    if (item == NULL && errcode != 1) {
      return NULL; /* db error */
    }
  }
  else if (citem && strcmp(citem, "FULL") == 0) {
    item = get_periodical(ptr_bibconns->conn_source, periodical, database, 0, &errcode, 0, my_dbi_result_get_idval(dbires, "refdb_id"), NULL /* no frequency required */);
    if (item == NULL && errcode != 1) {
      return NULL; /* db error */
    }
  }
  else if (citem && strcmp(citem, "USERONE") == 0) {
    item = get_periodical(ptr_bibconns->conn_source, periodical, database, 1, &errcode, 0, my_dbi_result_get_idval(dbires, "refdb_id"), NULL /* no frequency required */);
    if (item == NULL && errcode != 1) {
      return NULL; /* db error */
    }
  }
  else if (citem && strcmp(citem, "USERTWO") == 0) {
    item = get_periodical(ptr_bibconns->conn_source, periodical, database, 2, &errcode, 0, my_dbi_result_get_idval(dbires, "refdb_id"), NULL /* no frequency required */);
    if (item == NULL && errcode != 1) {
      return NULL; /* db error */
    }
  }

  if (!*periodical) { /* try the second choice */
    citem = dbi_result_get_string_idx(dbires_ref, JOURNALNAMEALTERNATETEXT);
    if (citem && strcmp(citem, "AABBREV") == 0) {
      item = get_periodical(ptr_bibconns->conn_source, periodical, database, 3, &errcode, 0, my_dbi_result_get_idval(dbires, "refdb_id"), NULL /* no frequency required */);
      if (item == NULL && errcode != 1) {
	return NULL; /* db error */
      }
    }
    else if (citem && strcmp(citem, "AFULL") == 0) {
      item = get_periodical(ptr_bibconns->conn_source, periodical, database, 0, &errcode, 0, my_dbi_result_get_idval(dbires, "refdb_id"), NULL /* no frequency required */);
      if (item == NULL && errcode != 1) {
	return NULL; /* db error */
      }
    }
    else if (citem && strcmp(citem, "AUSERONE") == 0) {
      item = get_periodical(ptr_bibconns->conn_source, periodical, database, 1, &errcode, 0, my_dbi_result_get_idval(dbires, "refdb_id"), NULL /* no frequency required */);
      if (item == NULL && errcode != 1) {
	return NULL; /* db error */
      }
    }
    else if (citem && strcmp(citem, "AUSERTWO") == 0) {
      item = get_periodical(ptr_bibconns->conn_source, periodical, database, 2, &errcode, 0, my_dbi_result_get_idval(dbires, "refdb_id"), NULL /* no frequency required */);
      if (item == NULL && errcode != 1) {
	return NULL; /* db error */
      }
    }
  }

  if (!*periodical) {
/*     (*ptr_ref)[0] = '\0'; */
    return *ptr_ref; /* no periodical specified */
  }

  /* reference has periodical data */
  nhave_content = 1;

  if (!n_title_as_author) {
    item = my_dbi_result_get_string_copy_idx(dbires_ref, JOURNALNAMEPRECEEDING);
    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
      if (n_intext != 3) {
	if (sgml_entitize(&item, n_ref_format) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  return NULL;
	}
	if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	  free(item);
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      else {
	char* escaped_item;
	
	if ((escaped_item = escape_latex_chars_copy(item, strlen(item))) == NULL) {
	  free(item);
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  return NULL;
	}
	
	if ((new_ref = mstrcat(*ptr_ref, escaped_item, ptr_ref_len, 0)) == NULL) {
	  free(item);
	  free(escaped_item);
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	  free(escaped_item);
	}
      }      
    }
    if (item) {
      free(item);
    }
  } /* end if not n_title_as_author */

  if (n_intext != 3) {
    if (n_ref_format == REFTEIX5) {
      if (print_elstart_x(ptr_ref, ptr_ref_len, "title", "level", "m", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
      }
    }
    else if (n_ref_format == REFRTF) {
      if (print_rtf_format(ptr_ref, ptr_ref_len, "JOURNALNAME", dbires_ref, 1 /* start group */) == NULL) {
	return NULL;
      }
    }
    else {
      if (print_elstart_x(ptr_ref, ptr_ref_len, "title", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
      }
    }
  } 

  /* do some formatting */
  mod_periodical = malloc(strlen(periodical)*2); /* extra spaces */
  if (!mod_periodical) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return NULL;
  }

  strcpy(mod_periodical, periodical);

  citem = dbi_result_get_string_idx(dbires_ref, JOURNALNAMEPUNCTUATION);
  if (citem && strcmp(citem, "SPACE") == 0) {
    /* replace all periods with spaces */
    while ((period = strchr(mod_periodical, (int)'.')) != NULL) {
      *period = ' ';
    }
    
    /* remove trailing space, if any */
    if (mod_periodical[strlen(mod_periodical) - 1] == ' ') {
      mod_periodical[strlen(mod_periodical) - 1] = '\0';
    }
  }
  else if (citem && strcmp(citem, "PERIODSPACE") == 0) {
    /* insert a space after each period */
    period = mod_periodical;
    while((period = strchr(period, (int)'.')) != NULL) {
      /* don't add a space if this is the last word */
      if (*(period+1) != '\0') {
	replace_char_string(period, ". ", 1);
	period++;
      }
      else {
	break;
      }
    }
  }
  /* else: database already contains the periodonly format */
    
  /* handle the case requirements */
  citem = dbi_result_get_string_idx(dbires_ref, JOURNALNAMECASE);
  if (citem && strcmp(citem, "UPPER") == 0) {
    strup(mod_periodical);
  }
  else if (citem && strcmp(citem, "LOWER") == 0) {
    strdn(mod_periodical);
  }
  else if (citem && strcmp(citem, "ICAPS") == 0) {
    stricap(mod_periodical);
  }
  /* else: asis needs no changes */
  
  /* see whether journal name ends with a period */
  if (mod_periodical[strlen(mod_periodical)-1] == '.') {
    nhave_trailing_dot++;
  }

  if (n_intext != 3) { /* not bibtex */
    if (sgml_entitize(&mod_periodical, n_ref_format) == NULL) {
      free(mod_periodical);
      return NULL;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, mod_periodical, ptr_ref_len, 0)) == NULL) {
      free(mod_periodical);
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  else { /* bibtex */
    char* escaped_item;

    if ((escaped_item = escape_latex_chars_copy(mod_periodical, strlen(mod_periodical))) == NULL) {
      free(mod_periodical);
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return NULL;
    }
	
    if ((new_ref = mstrcat(*ptr_ref, escaped_item, ptr_ref_len, 0)) == NULL) {
      free(mod_periodical);
      free(escaped_item);
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
      free(escaped_item);
    }
  }      

  free(mod_periodical);
  
  if (n_ref_format == REFRTF) {
    if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
      return NULL;
    }
  }
  else if (n_intext != 3) {
    if (print_elend_x(ptr_ref, ptr_ref_len, "title", ptr_indent, ns) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return NULL;
    }
  }
 
  if (!n_title_as_author) {
    item = my_dbi_result_get_string_copy_idx(dbires_ref, JOURNALNAMEFOLLOWING);
    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* following */
      if (n_intext != 3) {
	if (sgml_entitize(&item, n_ref_format) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  return NULL;
	}
	
	/* skip dot if there is already one */
	if (nhave_trailing_dot && *item == '.') {
	  if ((new_ref = mstrcat(*ptr_ref, item+1, ptr_ref_len, 0)) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    return NULL;
	  }
	  else {
	    *ptr_ref = new_ref;
	  }
	}
	else {
	  if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    return NULL;
	  }
	  else {
	    *ptr_ref = new_ref;
	  }
	}
      }
      else {
	char* escaped_item;
	
	if ((escaped_item = escape_latex_chars_copy(item, strlen(item))) == NULL) {
	  free(item);
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  return NULL;
	}
	
	/* skip dot if there is already one */
	if (nhave_trailing_dot && *escaped_item == '.') {
	  if ((new_ref = mstrcat(*ptr_ref, escaped_item+1, ptr_ref_len, 0)) == NULL) {
	    free(item);
	    free(escaped_item);
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    return NULL;
	  }
	  else {
	    *ptr_ref = new_ref;
	    free(escaped_item);
	  }
	}
	else {
	  if ((new_ref = mstrcat(*ptr_ref, escaped_item, ptr_ref_len, 0)) == NULL) {
	    free(item);
	    free(escaped_item);
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    return NULL;
	  }
	  else {
	    *ptr_ref = new_ref;
	    free(escaped_item);
	  }
	}
      }
    }
    if (item) {
      free(item);
    }
  } /* end if not n_title_as_author */

  if (!nhave_content) {
    (*ptr_ref)[0] = '\0';
  }
  return *ptr_ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_simple(): formats a simple part of a bibliography entry
                      as a DocBook bibliomixed text

  char* format_simple returns ptr to the buffer if successful, NULL if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least 4096 byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  dbi_result dbires ptr to a dbi result structure containing the
                    current reference

  dbi_result dbires_ref ptr to a dbi result structure containing the
                    reference style

  int n_intext  0 = bibliography citation; 1 = intext citation; 2 =
                    intext citation subsequent

  int n_item_index the index of the requested contents in dbires

  int n_preceeding_index the index of the preceeding stuff in dbires_ref

  int n_following_index the index of the following stuff in dbires_ref

  const char* elname the name of the element that receives the output

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* format_simple(char** ptr_ref, size_t* ptr_ref_len, struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_ref, int n_intext, int n_item_index, int n_preceeding_index, int n_following_index, const char* elname, const char* ns, struct xmlindent* ptr_indent, int n_ref_format) {
  int nhave_content = 0;
  char *new_ref;
  char *item_simple;
  char *item;

  item_simple = my_dbi_result_get_string_copy_idx(dbires, n_item_index);

  if (item_simple && *item_simple && !my_dbi_conn_error_flag(ptr_bibconns->conn_source)) { /* reference has requested data */
    nhave_content = 1;
    
    if (sgml_entitize(&item_simple, n_ref_format) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(item_simple);
      return NULL;
    }

    LOG_PRINT(LOG_DEBUG, elname);
 
    /* some elements need some special care */
    item = my_dbi_result_get_string_copy_idx(dbires_ref, n_preceeding_index);
    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
      if (sgml_entitize(&item, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	free(item_simple);
	return NULL;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	free(item_simple);
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    if (item) {
      free(item);
    }

    if (!strcmp(elname, "city")) {
      if (n_ref_format == REFTEIX5) {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "seg", "type", "pubplace", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
      }
      else if (n_ref_format == REFRTF) {
	if (print_rtf_format(ptr_ref, ptr_ref_len, "PUBPLACE", dbires_ref, 1 /* start group */) == NULL) {
	  return NULL;
	}
      }
      else {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "address", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
	if (print_elstart_x(ptr_ref, ptr_ref_len, "city", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
      }
    }

    /* obsolete? */
/*     else if (!strcmp(elname, "ulink")) { */
/*       if (n_ref_format == REFTEIX5) { */
/* 	if (print_elstart_x(ptr_ref, ptr_ref_len, "seg", "type", "ulink", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /\* not empty *\/, ptr_indent, ns) == NULL) { */
/* 	  LOG_PRINT(LOG_WARNING, get_status_msg(801)); */
/* 	  free(item_simple); */
/* 	  return NULL; */
/* 	} */
/*       } */
/*       else if (n_ref_format == REFRTF) { */
/* 	/\* todo: insert RTF stuff *\/ */
/*       } */
/*       else { */
/* 	if (print_elstart_x(ptr_ref, ptr_ref_len, "bibliomisc", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /\* not empty *\/, ptr_indent, ns) == NULL) { */
/* 	  LOG_PRINT(LOG_WARNING, get_status_msg(801)); */
/* 	  free(item_simple); */
/* 	  return NULL; */
/* 	} */
/* 	if (print_elstart_x(ptr_ref, ptr_ref_len, "ulink", "url", item_simple, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /\* not empty *\/, ptr_indent, ns) == NULL) { */
/* 	  LOG_PRINT(LOG_WARNING, get_status_msg(801)); */
/* 	  free(item_simple); */
/* 	  return NULL; */
/* 	} */
/*       } */
/*     } */
    else if (!strcmp(elname, "userdef1")
	     || !strcmp(elname, "userdef2")
	     || !strcmp(elname, "userdef3")
	     || !strcmp(elname, "userdef4")
	     || !strcmp(elname, "userdef5")
/* 	     || !strcmp(elname, "link0") */
/* 	     || !strcmp(elname, "link1") */
/* 	     || !strcmp(elname, "link2") */
/* 	     || !strcmp(elname, "link3") */
/* 	     || !strcmp(elname, "link4") */
	     || !strcmp(elname, "typeofwork")
	     || !strcmp(elname, "area")
	     || !strcmp(elname, "ostype")
	     || !strcmp(elname, "degree")
	     || !strcmp(elname, "runningtime")
	     || !strcmp(elname, "classcodeintl")
	     || !strcmp(elname, "classcodeus")
	     || !strcmp(elname, "senderemail")
	     || !strcmp(elname, "recipientemail")
	     || !strcmp(elname, "mediatype")
	     || !strcmp(elname, "numvolumes")
	     || !strcmp(elname, "edition")
	     || !strcmp(elname, "computer")
	     || !strcmp(elname, "conferencelocation")
	     || !strcmp(elname, "registrynum")
	     || !strcmp(elname, "classification")
	     || !strcmp(elname, "section")
	     || !strcmp(elname, "pamphletnum")
	     || !strcmp(elname, "chapternum")
) {
      if (n_ref_format == REFTEIX5) {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "seg", "type", elname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
      }
      else if (n_ref_format == REFRTF) {
	char* ucelname;
	if ((ucelname = strdup(elname)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
	
	strup(ucelname);

	if (print_rtf_format(ptr_ref, ptr_ref_len, ucelname, dbires_ref, 1 /* start group */) == NULL) {
	  return NULL;
	}
	free(ucelname);
      }
      else {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "bibliomset", "role", elname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
      }
    }
    else if (!strcmp(elname, "volumenum")) {
      if (n_ref_format == REFTEIX5) {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "seg", "type", "volume", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
      }
      else if (n_ref_format == REFRTF) {
	/* todo: insert RTF stuff */
    if (print_rtf_format(ptr_ref, ptr_ref_len, "VOLUME", dbires_ref, 1 /* start group */) == NULL) {
      return NULL;
    }
      }
      else {
	if (print_elstart_x(ptr_ref, ptr_ref_len, elname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
      }
    }
    else if (!strcmp(elname, "issuenum")) {
      if (n_ref_format == REFTEIX5) {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "seg", "type", "issue", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
      }
      else if (n_ref_format == REFRTF) {
	/* todo: insert RTF stuff */
	if (print_rtf_format(ptr_ref, ptr_ref_len, "ISSUE", dbires_ref, 1 /* start group */) == NULL) {
	  return NULL;
	}
      }
      else {
	if (print_elstart_x(ptr_ref, ptr_ref_len, elname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
      }
    }
    else if (!strcmp(elname, "publishername")) {
      if (n_ref_format == REFTEIX5) {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "seg", "type", "publisher", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
      }
      else if (n_ref_format == REFRTF) {
	/* todo: insert RTF stuff */
	if (print_rtf_format(ptr_ref, ptr_ref_len, "PUBLISHER", dbires_ref, 1 /* start group */) == NULL) {
	  return NULL;
	}
      }
      else {
	if (print_elstart_x(ptr_ref, ptr_ref_len, elname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
      }
    }
    else {
      if (n_ref_format == REFRTF) {
	char* ucelname;
	if ((ucelname = strdup(elname)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
	
	strup(ucelname);

	if (print_rtf_format(ptr_ref, ptr_ref_len, ucelname, dbires_ref, 1 /* start group */) == NULL) {
	  return NULL;
	}
	free(ucelname);
      }
      else if (print_elstart_x(ptr_ref, ptr_ref_len, elname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item_simple);
	return NULL;
      }
    }

    if ((new_ref = mstrcat(*ptr_ref, item_simple, ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(item_simple);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
 
    if (n_ref_format == REFRTF) {
      if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
	return NULL;
      }
    }
    else {
      if (!strcmp(elname, "city")) {
	if (n_ref_format == REFTEIX5) {
	  if (print_elend_x(ptr_ref, ptr_ref_len, "seg", ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
	else {
	  if (print_elend_x(ptr_ref, ptr_ref_len, "city", ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	  if (print_elend_x(ptr_ref, ptr_ref_len, "address", ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
      }
      else if (!strcmp(elname, "ulink")) {
	if (n_ref_format == REFTEIX5) {
	  if (print_elend_x(ptr_ref, ptr_ref_len, "seg", ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
	else {
	  if (print_elend_x(ptr_ref, ptr_ref_len, "ulink", ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	  if (print_elend_x(ptr_ref, ptr_ref_len, "bibliomisc", ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
      }
      else if (!strcmp(elname, "userdef1")
	       || !strcmp(elname, "userdef2")
	       || !strcmp(elname, "userdef3")
	       || !strcmp(elname, "userdef4")
	       || !strcmp(elname, "userdef5")
/* 	       || !strcmp(elname, "link0") */
/* 	       || !strcmp(elname, "link1") */
/* 	       || !strcmp(elname, "link2") */
/* 	       || !strcmp(elname, "link3") */
/* 	       || !strcmp(elname, "link4") */
	       || !strcmp(elname, "typeofwork")
	       || !strcmp(elname, "area")
	       || !strcmp(elname, "ostype")
	       || !strcmp(elname, "degree")
	       || !strcmp(elname, "runningtime")
	       || !strcmp(elname, "classcodeintl")
	       || !strcmp(elname, "classcodeus")
	       || !strcmp(elname, "senderemail")
	       || !strcmp(elname, "recipientemail")
	       || !strcmp(elname, "mediatype")
	       || !strcmp(elname, "numvolumes")
	       || !strcmp(elname, "edition")
	       || !strcmp(elname, "computer")
	       || !strcmp(elname, "conferencelocation")
	       || !strcmp(elname, "registrynum")
	       || !strcmp(elname, "classification")
	       || !strcmp(elname, "section")
	       || !strcmp(elname, "pamphletnum")
	       || !strcmp(elname, "chapternum")
	       ) {
	if (n_ref_format == REFTEIX5) {
	  if (print_elend_x(ptr_ref, ptr_ref_len, "seg", ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
	else {
	  if (print_elend_x(ptr_ref, ptr_ref_len, "bibliomset", ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
      }
      else if (!strcmp(elname, "volumenum")) {
	if (n_ref_format == REFTEIX5) {
	  if (print_elend_x(ptr_ref, ptr_ref_len, "seg", ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
	else {
	  if (print_elend_x(ptr_ref, ptr_ref_len, elname, ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
      }
      else if (!strcmp(elname, "issuenum")) {
	if (n_ref_format == REFTEIX5) {
	  if (print_elend_x(ptr_ref, ptr_ref_len, "seg", ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
	else {
	  if (print_elend_x(ptr_ref, ptr_ref_len, elname, ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
      }
      else if (!strcmp(elname, "publishername")) {
	if (n_ref_format == REFTEIX5) {
	  if (print_elend_x(ptr_ref, ptr_ref_len, "seg", ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
	else {
	  if (print_elend_x(ptr_ref, ptr_ref_len, elname, ptr_indent, ns) == NULL) {
	    LOG_PRINT(LOG_WARNING, get_status_msg(801));
	    free(item_simple);
	    return NULL;
	  }
	}
      }
      else {
	if (print_elend_x(ptr_ref, ptr_ref_len, elname, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item_simple);
	  return NULL;
	}
      }
    } /* end if not RTF */

    item = my_dbi_result_get_string_copy_idx(dbires_ref, n_following_index);
    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* following */
      if (sgml_entitize(&item, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	free(item_simple);
	return NULL;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	free(item_simple);
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    if (item) {
      free(item);
    }
  }
  if (item_simple) {
    free(item_simple);
  }

  if (!nhave_content) {
    (*ptr_ref)[0] = '\0';
  }
  return *ptr_ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_pages(): formats the pages part of a bibliography entry
                      as a DocBook bibliomixed text

  char* format_pages returns ptr to the buffer if successful, NULL if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least 4096 byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  struct BIBCONNS* ptr_bibconns ptr to structure with database connections

  dbi_result dbires ptr to a dbi result structure containing the
                    current reference

  dbi_result dbires_ref ptr to a dbi result structure containing
                    the reference style

  int n_intext  0 = bibliography citation; 1 = intext citation; 2 =
                    intext citation subsequent

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* format_pages(char** ptr_ref, size_t* ptr_ref_len, struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_ref, int n_intext, const char* ns, struct xmlindent* ptr_indent, int n_ref_format) {
  int nhave_content = 0;
  char *new_ref;
  char new_endpage[256];
  char *item_sp;
  char *item_ep;
  char *item;
  const char *item_type;
  struct lilimem sentinel;
/*   dbi_conn conn; */

/*   conn = dbi_result_get_conn(dbires_ref); */

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

  item_sp = my_dbi_result_get_string_copy_idx(dbires, REFDB_STARTPAGE);
  if (item_sp  && my_dbi_conn_error_flag(ptr_bibconns->conn_source)) {
    free(item_sp);
    item_sp = NULL;
  }
  else if (item_sp) {
    if (insert_lilimem(&sentinel, (void**)&item_sp, NULL)) {
      return NULL;
    }
  }

  item_ep = my_dbi_result_get_string_copy_idx(dbires, REFDB_ENDPAGE);
  if (item_ep  && my_dbi_conn_error_flag(ptr_bibconns->conn_source)) {
    free(item_ep);
    item_ep = NULL;
  }
  else if (item_ep) {
    if (insert_lilimem(&sentinel, (void**)&item_ep, NULL)) {
      delete_all_lilimem(&sentinel);
      return NULL;
    }
  }

  if (item_sp && *item_sp) { /* reference has at least start page data */
    nhave_content = 1;
    item = my_dbi_result_get_string_copy_idx(dbires_ref, PAGESPRECEEDING);
    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
      if (sgml_entitize(&item, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	delete_all_lilimem(&sentinel);
	return NULL;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	delete_all_lilimem(&sentinel);
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    if (item) {
      free(item);
    }

    /* if we only have the start page or the format says to only use the start page, forget about the end page */
    item_type = dbi_result_get_string_idx(dbires_ref, PAGESPAGERANGETYPE);
    if (!item_ep || !*item_ep || (item_type && strcmp(item_type, "STARTONLY") == 0)) {

      item = my_dbi_result_get_string_copy_idx(dbires_ref, PAGESSINGLEPAGEPRECEEDING);

      if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	if (sgml_entitize(&item, n_ref_format) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      
	if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      if (item) {
	free(item);
      }

      if (n_ref_format == REFTEIX5) {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "seg", "type", "pages", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      }
      else if (n_ref_format == REFRTF) {
	/* todo: insert RTF stuff */
	if (print_rtf_format(ptr_ref, ptr_ref_len, "PAGES", dbires_ref, 1 /* start group */) == NULL) {
	  return NULL;
	}
      }
      else {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "pagenums", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      }
 
      if (sgml_entitize(&item_sp, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	delete_all_lilimem(&sentinel);
	return NULL;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, item_sp, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	delete_all_lilimem(&sentinel);
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }

      if (n_ref_format == REFTEIX5) {
	if (print_elend_x(ptr_ref, ptr_ref_len, "seg", ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      }
      else if (n_ref_format == REFRTF) {
	if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
	  return NULL;
	}
      }
      else {
	if (print_elend_x(ptr_ref, ptr_ref_len, "pagenums", ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      }
 
      item = my_dbi_result_get_string_copy_idx(dbires_ref, PAGESSINGLEPAGEFOLLOWING);
      if (item && *item & !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	if (sgml_entitize(&item, n_ref_format) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      
	if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      if (item) {
	free(item);
      }
    }
    /* we have both start- and end-pages and we're supposed to render both */
    else if (item_ep && *item_ep) {
      item = my_dbi_result_get_string_copy_idx(dbires_ref, PAGESPAGERANGEPRECEEDING);
      if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	if (sgml_entitize(&item, n_ref_format) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      
	if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      if (item) {
	free(item);
      }

      if (n_ref_format == REFTEIX5) {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "seg", "type", "pages", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      }
      else if (n_ref_format == REFRTF) {
	if (print_rtf_format(ptr_ref, ptr_ref_len, "PAGES", dbires_ref, 1 /* start group */) == NULL) {
	  return NULL;
	}
      }
      else {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "pagenums", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      }
 
      if (sgml_entitize(&item_sp, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	delete_all_lilimem(&sentinel);
	return NULL;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, item_sp, ptr_ref_len, 0)) == NULL) {
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }

      /* if RANGESEPARATOR is defined, use it (even if it is the empty string)
         otherwise use a dash */
      item = my_dbi_result_get_string_copy_idx(dbires_ref, PAGESPAGERANGERANGESEPARATOR);
      if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	if (sgml_entitize(&item, n_ref_format) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      
	if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      else {
	if ((new_ref = mstrcat(*ptr_ref, "-", ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      if (item) {
	free(item);
      }

      if ((new_ref = mstrcat(*ptr_ref, normalize_pages(new_endpage, item_sp, item_ep, item_type), ptr_ref_len, 0)) == NULL) {
	delete_all_lilimem(&sentinel);
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }

      if (n_ref_format == REFTEIX5) {
	if (print_elend_x(ptr_ref, ptr_ref_len, "seg", ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      }
      else if (n_ref_format == REFRTF) {
	/* todo: insert RTF stuff */
	if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
	  return NULL;
	}
      }
      else {
	if (print_elend_x(ptr_ref, ptr_ref_len, "pagenums", ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      }
 
      item = my_dbi_result_get_string_copy_idx(dbires_ref, PAGESPAGERANGEFOLLOWING);
      if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
	if (sgml_entitize(&item, n_ref_format) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
      
	if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  free(item);
	  delete_all_lilimem(&sentinel);
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      if (item) {
	free(item);
      }
    }
    /* else: no endpage data available */
 
    item = my_dbi_result_get_string_copy_idx(dbires_ref, PAGESFOLLOWING);
    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* following */
      if (sgml_entitize(&item, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	delete_all_lilimem(&sentinel);
	return NULL;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	delete_all_lilimem(&sentinel);
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    if (item) {
      free(item);
    }
  }

  delete_all_lilimem(&sentinel);

  if (!nhave_content) {
    (*ptr_ref)[0] = '\0';
  }
  return *ptr_ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_notes(): formats the notes part of a bibliography entry
                      as a DocBook bibliomixed text

  char* format_notes returns NULL on error, ptr to ref if ok. ref may
                     be reallocated during the rendering, so it is
                     essential that upon return of this function *ONLY*
                     the returned pointer is used to address the result
                     and *NOT* the original pointer which may be
                     invalid.

  char** ptr_ref ptr to a ptr to a malloc()'ed string that will receive
                     the output
                     *ptr_ref will be reallocated if necessary

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  struct BIBCONNS* ptr_bibconns ptr to structure with database connections

  dbi_result dbires ptr to a dbi result structure containing the
                    current reference

  dbi_result dbires_ref ptr to a dbi result structure containing
                    the reference style

  const char* username ptr to string with the current user

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* format_notes(char** ptr_ref, size_t* ptr_ref_len, struct BIBCONNS* ptr_bibconns, dbi_result dbires, dbi_result dbires_ref, const char* username, const char* ns, struct xmlindent* ptr_indent, int n_ref_format) {
  int nhave_content = 0;
  char *new_ref;
  char *item;
  char *item_notes;

  item_notes = get_notes_copy(dbires, (char*)username);

  if (item_notes && *item_notes) {
    nhave_content = 1;
    
    item = my_dbi_result_get_string_copy_idx(dbires_ref, NOTESPRECEEDING);
    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* preceeding */
      if (sgml_entitize(&item, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	free(item_notes);
	return NULL;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	free(item_notes);
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    if (item) {
      free(item);
    }

    if (n_ref_format == REFTEIX5) {
      if (print_elstart_x(ptr_ref, ptr_ref_len, "seg", "type", "notes", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item_notes);
	return NULL;
      }
    }
    else if (n_ref_format == REFRTF) {
      if (print_rtf_format(ptr_ref, ptr_ref_len, "NOTES", dbires_ref, 1 /* start group */) == NULL) {
	return NULL;
      }
    }
    else {
      if (print_elstart_x(ptr_ref, ptr_ref_len, "bibliomset", "role", "notes", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item_notes);
	return NULL;
      }
    }
 
    if (sgml_entitize(&item_notes, n_ref_format) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(item_notes);
      return NULL;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, item_notes, ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(item_notes);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
 
    if (n_ref_format == REFTEIX5) {
      if (print_elend_x(ptr_ref, ptr_ref_len, "seg", ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item_notes);
	return NULL;
      }
    }
    else if (n_ref_format == REFRTF) {
      if (print_rtf_end_group(ptr_ref, ptr_ref_len) == NULL) {
	return NULL;
      }
    }
    else {
      if (print_elend_x(ptr_ref, ptr_ref_len, "bibliomset", ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item_notes);
	return NULL;
      }
    }
 
    item = my_dbi_result_get_string_copy_idx(dbires_ref, NOTESFOLLOWING);
    if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) { /* following */
      if (sgml_entitize(&item, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	free(item_notes);
	return NULL;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, item, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(item);
	free(item_notes);
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    if (item) {
      free(item);
    }
  }

  if (item_notes) {
    free(item_notes);
  }

  if (!nhave_content) {
    (*ptr_ref)[0] = '\0';
  }
  return *ptr_ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_separator(): formats the separator part of a bibliography entry
                      as a DocBook bibliomixed text

  char* format_separator returns NULL on error, ptr to ref if ok. ref may
                     be reallocated during the rendering, so it is
                     essential that upon return of this function *ONLY*
                     the returned pointer is used to address the result
                     and *NOT* the original pointer which may be
                     invalid.

  char* ref ptr to an malloc()'ed string that will receive the output
                     ref will be reallocated if necessary

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  struct BIBCONNS* ptr_bibconns ptr to structure with database connections

  unsigned int separator_id  the ID of the separator

  int n_ref_format REFXX type of output

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* format_separator(char* ref, size_t* ptr_ref_len, struct BIBCONNS* ptr_bibconns, unsigned int n_separator_id, int n_ref_format) {
  int nhave_content = 0;
  char *new_ref;
  char *item;
  const char* drivername;
  char sql_command[256];
  dbi_result dbires_sep;

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

  if (!strcmp(my_dbi_conn_get_cap(ptr_bibconns->conn_refdb, "multiple_db"), "t")) {
    sprintf(sql_command, "SELECT VALUE from %s.SEPARATORS where ID=%u", main_db, n_separator_id);
  }
  else {
    sprintf(sql_command, "SELECT VALUE from SEPARATORS where ID=%u", n_separator_id);
  }

  LOG_PRINT(LOG_DEBUG, sql_command);
  dbires_sep = dbi_conn_query(ptr_bibconns->conn_refdb, sql_command);
  if (!dbires_sep) {
    LOG_PRINT(LOG_WARNING, "SEPARATORS select failed");
    return NULL;
  }
  
  if (dbi_result_next_row(dbires_sep) == 0) {
    LOG_PRINT(LOG_WARNING, "SEPARATORS select failed");
    dbi_result_free(dbires_sep);
    return NULL;
  }

  item = my_dbi_result_get_string_copy(dbires_sep, "VALUE");

  if (item && *item && !my_dbi_conn_error_flag(ptr_bibconns->conn_refdb)) {
    nhave_content = 1;

    if (sgml_entitize(&item, n_ref_format) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(item);
      dbi_result_free(dbires_sep);
      return NULL;
    }
    if ((new_ref = mstrcat(ref, item, ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      dbi_result_free(dbires_sep);
      free(item);
      return NULL;
    }
    else {
      ref = new_ref;
    }
  }
  if (item) {
    free(item);
  }

  dbi_result_free(dbires_sep);
/*    printf("end separators\n"); */
  if (!nhave_content) {
    ref[0] = '\0';
  }
  return ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_year(): formats the year part of a pubdate in a bibliography
                     entry

  static char* format_year returns NULL on error, ptr to ref if ok. ref may
                     be reallocated during the rendering, so it is
                     essential that upon return of this function *ONLY*
                     the returned pointer is used to address the result
                     and *NOT* the original pointer which may be
                     invalid.

  char* ref ptr to an malloc()'ed string that will receive the output
                     ref will be reallocated if necessary

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  unsigned short n_year  the publication year

  const char* yearformat ptr to a string containing the formatting info

  const char* pad ptr to a string containing the padding info

  const char* unique_suffix ptr to a string which makes the year unique

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* format_year(char* ref, size_t* ptr_ref_len, unsigned short n_year, const char* yearformat, const char* pad, const char* unique_suffix) {
  char* new_ref;
  char the_year[32] = "";
  char year_string[32];

/*    printf("in format_year: year=%s; unique_suffix=%s<<\n", year, unique_suffix); */
/*    fflush(stdout); */

  sprintf(year_string, "%d", n_year);

  if (yearformat && strcmp(yearformat, "FOURDIGIT") == 0) {
    if (strcmp(pad, "YY") == 0) {
      /* yes I know there will be a Y10k problem... */
      strcpy(the_year, "0000");
      strcpy(the_year+(4-strlen(year_string)), year_string);
    }
    else {
      strcpy(the_year, year_string);
    }
  }
  else if (yearformat && strcmp(yearformat, "TWODIGIT") == 0) {
    if (strcmp(pad, "YY") == 0) {
      if (strlen(year_string) == 1) {
	strcpy(the_year, "00");
	strcpy(the_year+1, year_string);
      }
      else {
	/* take only last two digits */
	strncpy(the_year, year_string+strlen(year_string)-2, 2);
	the_year[2] = '\0';
      }
    }
    else {
      if (strlen(year_string) == 1) {
	strcpy(the_year, year_string);
      }
      else {
	/* take only last two digits */
	strncpy(the_year, year_string+strlen(year_string)-2, 2);
	the_year[2] = '\0';
      }
    }
  }

  else if (yearformat && strcmp(yearformat, "ROMANYEAR") == 0) {
    arabic_to_roman(the_year, year_string);
  }

  if (unique_suffix && *unique_suffix) {
    strcat(the_year, unique_suffix);
  }

  if ((new_ref = mstrcat(ref, the_year, ptr_ref_len, 0)) == NULL) {
    return NULL;
  }
  else {
    ref = new_ref;
  }
/*    printf("done with format_year. ref=%s<<\n", ref); */
  return ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_month(): formats the month part of a pubdate in a bibliography
                     entry

  staticchar* format_month returns NULL on error, ptr to ref if ok. ref may
                     be reallocated during the rendering, so it is
                     essential that upon return of this function *ONLY*
                     the returned pointer is used to address the result
                     and *NOT* the original pointer which may be
                     invalid.

  char* ref ptr to an malloc()'ed string that will receive the output
                     ref will be reallocated if necessary

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  const char* month ptr to a string containing the RIS PY /MM/DD/otherinfo part

  const char* monthformat ptr to a string containing the formatting info

  const char* pad ptr to a string containing the padding info

  dbi_result dbires_cit ptr to a dbi result structure containing
                    the citation style

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* format_month(char* ref, size_t* ptr_ref_len, const char* month, const char* monthformat, const char* pad, dbi_result dbires_cit) {
  char* slash;
  char* new_ref;
  char* monthstring;
  const char* item_full;
  const char* item_abbrev;
  const char* item_threelet;
  dbi_conn conn;
  char the_month[3];
  char fullmonth[12][9] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
  char threelettermonth[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  /* ToDo: dunno if the following abbreviations are correct */
  char abbrevmonth[12][6] = {"Jan.", "Feb.", "March", "April", "May", "June", "July", "Aug.", "Sept.", "Oct.", "Nov.", "Dec."};
  char romanmonth[12][10] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"};
  int i = 0;
  int month_len;
  int n_month;
  char* slashes[3] = {NULL, NULL, NULL}; /* only the first three slashes are relevant, if at all */

  conn = dbi_result_get_conn(dbires_cit);

  slash = strchr(month, (int)'/');
  
  /* fill the slashes array with up to three slashes. Only the first three may be relevant, as the others would be part of the otherinfo string */
  while (slash != NULL && i < 3) {
    slashes[i] = slash;
    i++;
    if (*(slash+1)) {
      slash = strchr(slash+1, (int)'/');
    }
    else {
      break;
    }
  }

  /* we need at least two slashes to extract the month. This can be a one- or two-digit number. If its longer or shorter, we can't safely handle it */
  if (slashes[0] && slashes[1]) {
    month_len = slashes[1] - slashes[0];
    if (month_len == 2) {
      if (strcmp(pad, "YY") == 0) {
	the_month[0] = '0';
	the_month[1] = *(slashes[0]+1);
	the_month[2] = '\0';
      }
      else {
	the_month[0] = *(slashes[0]+1);
	the_month[1] = '\0';
      }
    }
    else if (month_len == 3 && *(slashes[0]+1) == '0' && strcmp(pad, "NN") == 0) {
      the_month[0] = *(slashes[0]+2);
      the_month[1] = '\0';
    }
    else if (month_len == 3) {
      strncpy(the_month, slashes[0]+1, 2);
      the_month[2] = '\0';
    }
    else { /* month was not existent or improperly formatted */
      return ref;
    }

    /* sanity test */
    n_month = atoi(the_month);
    if (n_month < 1 || n_month > 12) {
      return ref;
    }

    item_full = dbi_result_get_string_idx(dbires_cit, n_month + JANFULL - 1);
    if (item_full && my_dbi_conn_error_flag(conn)) {
      item_full = NULL;
    }
    item_abbrev = dbi_result_get_string_idx(dbires_cit, n_month + JANABBREV - 1);
    if (item_abbrev && my_dbi_conn_error_flag(conn)) {
      item_abbrev = NULL;
    }
    item_threelet = dbi_result_get_string_idx(dbires_cit, n_month + JANTHREELET - 1);
    if (item_threelet && my_dbi_conn_error_flag(conn)) {
      item_threelet = NULL;
    }

    /* do the actual formatting */
    /* ptr_cit_row points to the result of a query for citstyle stuff */
    if (strcmp(monthformat, "FULL") == 0) {
      monthstring = (item_full && *item_full) ? (char*)item_full : fullmonth[n_month-1];
      if ((new_ref = mstrcat(ref, monthstring, ptr_ref_len, 0)) == NULL) {
	return NULL;
      }
      else {
	ref = new_ref;
      }
    }
    else if (strcmp(monthformat, "ABBREVDOT") == 0) {
      monthstring = (item_abbrev && *item_abbrev) ? (char*)item_abbrev : abbrevmonth[n_month-1];
      if ((new_ref = mstrcat(ref, monthstring, ptr_ref_len, 0)) == NULL) {
	return NULL;
      }
      else {
	ref = new_ref;
      }
    }
    else if (strcmp(monthformat, "THREELETTER") == 0) {
      monthstring = (item_threelet && *item_threelet) ? (char*)item_threelet : threelettermonth[n_month-1];
      if ((new_ref = mstrcat(ref, monthstring, ptr_ref_len, 0)) == NULL) {
	return NULL;
      }
      else {
	ref = new_ref;
      }
    }
    else if (strcmp(monthformat, "ARABICMONTH") == 0) {
      if ((new_ref = mstrcat(ref, the_month, ptr_ref_len, 0)) == NULL) {
	return NULL;
      }
      else {
	ref = new_ref;
      }
    }
    else if (strcmp(monthformat, "ROMANMONTH") == 0) {
      if ((new_ref = mstrcat(ref, romanmonth[n_month-1], ptr_ref_len, 0)) == NULL) {
	return NULL;
      }
      else {
	ref = new_ref;
      }
    }
  }

  return ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_day(): formats the day part of a pubdate in a bibliography
                     entry

  static char* format_day returns NULL on error, ptr to ref if ok. ref may
                     be reallocated during the rendering, so it is
                     essential that upon return of this function *ONLY*
                     the returned pointer is used to address the result
                     and *NOT* the original pointer which may be
                     invalid.

  char* ref ptr to an malloc()'ed string that will receive the output
                     ref will be reallocated if necessary

  size_t* ptr_ref_len ptr to an int holding the current length of ref.
                     Will be modified if ref is reallocated.

  const char* day ptr to a string containing the RIS PY /MM/DD/otherinfo part

  const char* dayformat ptr to a string containing the formatting info

  const char* pad ptr to a string containing the padding info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* format_day(char* ref, size_t* ptr_ref_len, const char* day, const char* dayformat, const char* pad) {
  char* slash;
  char* new_ref;
  char the_day[3];
  char romanday[31][7] = {"i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x", "xi", "xii", "xiii", "xiv", "xv", "xvi", "xvii", "xviii", "xix", "xx", "xxi", "xxii", "xxiii", "xxiv", "xxv", "xxvi", "xxvii", "xxviii", "xxix", "xxx", "xxxi"};
  char* slashes[3] = {NULL, NULL, NULL}; /* only the first three slashes are relevant, if at all */
  int i = 0;
  int day_len;
  int n_day;

  slash = strchr(day, (int)'/');
  
  /* fill the slashes array with up to three slashes. Only the first three may be relevant, as the others would be part of the otherinfo string */
  while (slash != NULL && i < 3) {
    slashes[i] = slash;
    i++;
    if (*(slash+1)) {
      slash = strchr(slash+1, (int)'/');
    }
    else {
      break;
    }
  }
  
  /* for the day we need the second and third slash. Between them may be a one- or two-digit number. Anything smaller or larger cannot be formatted safely */
  if (slashes[1] && slashes[2]) {
    day_len = slashes[2] - slashes[1];
    if (day_len == 2) {
      if (strcmp(pad, "YY") == 0) {
	the_day[0] = '0';
	the_day[1] = *(slashes[1]+1);
	the_day[2] = '\0';
      }
      else {
	the_day[0] = *(slashes[1]+1);
	the_day[1] = '\0';
      }
    }
    else if (day_len == 3 && *(slashes[1]+1) == '0' && strcmp(pad, "NN") == 0) {
      the_day[0] = *(slashes[1]+2);
      the_day[1] = '\0';
    }
    else if (day_len == 3) {
      strncpy(the_day, slashes[1]+1, 2);
      the_day[2] = '\0';
    }
    else { /* day was not existent or improperly formatted */
      return ref;
    }

    /* sanity test */
    n_day = atoi(the_day);
    if (n_day < 1 || n_day > 31) {
      return ref;
    }

    /* do the actual formatting */
    if (strcmp(dayformat, "ARABICDAY") == 0) {
      if ((new_ref = mstrcat(ref, the_day, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
      }
      else {
	ref = new_ref;
      }
    }
    else if (strcmp(dayformat, "ROMANDAY") == 0) {
      if ((new_ref = mstrcat(ref, romanday[n_day-1], ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
      }
      else {
	ref = new_ref;
      }
    }
  }

  return ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  arabic_to_roman(): converts an arabic number between 0 and 3999 (incl)
                     to a roman numeral

  static char* arabic_to_roman returns the result string (roman)

  char* roman ptr to a buffer which receives the roman output. The
                     buffer must hold at least 16 chars (incl. '\0')

  const char* arabic ptr to a buffer containing the arabic number to convert

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* arabic_to_roman(char* roman, const char* arabic) {
  int len;
  char romandigit[4][3][2] = {
    {{'I', '\0'}, {'V', '\0'}, {'X', '\0'}},
    {{'X', '\0'}, {'L', '\0'}, {'C', '\0'}},
    {{'C', '\0'}, {'D', '\0'}, {'M', '\0'}},
    {{'M', '\0'}, {'\0', '\0'}, {'\0', '\0'}}
  };

  /* start with a clean string */
  *roman = '\0';

  /* give up if the number cannot be nicely formatted */
  if (atoi(arabic) >= 4000) {
    return roman;
  }

  /* the romandigit array contains 4 sets of strings for the values of 1, 5, and 10 times 1, 10, 100, and 1000. We simply walk through the arabic string from left to right (i.e. 1000s, 100s, 10s, 1s) and assemble the roman string by translating the digit into a series of roman digits, following the usual subtraction and addition rules. The algorithm silently discards all non-numeric characters including minus signs */
  /* a good source for the rules for roman numerals can be found at: */
  /* http://netdirect.net/~charta/Roman_numerals.html */
  while((len = strlen(arabic)) > 0) {
    switch (*arabic) {
    case '1':
      strcat(roman, romandigit[len-1][0]);
      break;
    case '2':
      strcat(roman, romandigit[len-1][0]);
      strcat(roman, romandigit[len-1][0]);
      break;
    case '3':
      strcat(roman, romandigit[len-1][0]);
      strcat(roman, romandigit[len-1][0]);
      strcat(roman, romandigit[len-1][0]);
      break;
    case '4':
      strcat(roman, romandigit[len-1][0]);
      strcat(roman, romandigit[len-1][1]);
      break;
    case '5':
      strcat(roman, romandigit[len-1][1]);
      break;
    case '6':
      strcat(roman, romandigit[len-1][1]);
      strcat(roman, romandigit[len-1][0]);
      break;
    case '7':
      strcat(roman, romandigit[len-1][1]);
      strcat(roman, romandigit[len-1][0]);
      strcat(roman, romandigit[len-1][0]);
      break;
    case '8':
      strcat(roman, romandigit[len-1][1]);
      strcat(roman, romandigit[len-1][0]);
      strcat(roman, romandigit[len-1][0]);
      strcat(roman, romandigit[len-1][0]);
      break;
    case '9':
      strcat(roman, romandigit[len-1][0]);
      strcat(roman, romandigit[len-1][2]);
      break;
    }
    arabic++;
  }
  return roman;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  normalize_pages(): formats endpage according to the format spec

  static char* normalize_pages returns the normalized endpage string

  char* new_endpage ptr to a buffer large enough to receive the 
            modified endpage. Should hold at least 256 chars (incl \0)

  const char* startpage ptr to a buffer containing the startpage

  const char* endpage ptr to a buffer containing the original endpage

  const char* format ptr to a buffer containing the formatting instructions

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* normalize_pages(char* new_endpage, const char* startpage, const char* endpage, const char* format) {
  int n_startpage;
  int n_endpage;
  int n_testpage;
  int i;
  char my_startpage[256];
  char my_endpage[256];
  char startpage_leading_slack[256];
  char endpage_leading_slack[256];
  char endpage_trailing_slack[256];
  char* ptr_start_start;
  char* ptr_end_start;
  char* ptr_start;
  char* ptr_end;
  char* ptr_end_end;

  if (!startpage || !endpage) {
    return NULL;
  }

  /* ToDo: this algorithm currently drops all non-numeric information in the page strings. This is not good for e.g. Am.J.Physiol. which uses the page numbers to denote the subdivision (e.g. H1223 for a paper in the heart and circulation section). The algorithm should be extended to leave those non-numeric parts alone */

  ptr_start_start = strpbrk(startpage, "123456789");
  ptr_end_start = strpbrk(endpage, "123456789");

  /* if the strings don't contain numbers, we just leave them as they are */
  if (!ptr_start_start || !ptr_end_start) {
    return (char*)endpage;
  }

  strncpy(startpage_leading_slack, startpage, ptr_start_start - startpage);
  startpage_leading_slack[ptr_start_start - startpage] = '\0';

  strncpy(endpage_leading_slack, endpage, ptr_end_start - endpage);
  endpage_leading_slack[ptr_end_start - endpage] = '\0';

  /* find the end of the number in the string */
  i = 0;
  while (ptr_end_start[i] != '\0' && isdigit((int)ptr_end_start[i])) {
    i++;
  }

  strcpy(endpage_trailing_slack, &ptr_end_start[i]); /* may be empty string */

  /* atoi and then sprintf removes any trailing slack */
  n_startpage = atoi(ptr_start_start);
  if (n_startpage < 0) {
    return (char*)endpage; /* don't even try any formatting here */
  }
  else {
    sprintf(my_startpage, "%d", n_startpage);
  }

  n_endpage = atoi(ptr_end_start);
  if (n_endpage < 0) {
    return (char*)endpage; /* don't try any formatting here */
  }
  else {
    sprintf(my_endpage, "%d", n_endpage);
  }

  /* if the endpage is not fully qualified (i.e. the numeric value is smaller than the value of startpage), we have to expand it. We simply use the cropped startpage string as a template and overwrite whatever part is specified in the cropped endpage string*/
  if (n_startpage > n_endpage) {
    strcpy(new_endpage, my_startpage);
    strcpy(new_endpage+strlen(my_startpage)-strlen(my_endpage), my_endpage);
    n_endpage = atoi(new_endpage);

    /* now we have to correct the endpage string if e.g. start=99, end=3. So far we get 93 which is dead wrong. We need 103 instead */
    n_testpage = n_endpage;
    i = 10;

    while (n_startpage > n_testpage) {
      n_testpage = n_endpage + i;
      i *= 10;
    }
    n_endpage = n_testpage;
    sprintf(new_endpage, "%d", n_endpage);
  }
  else {
    strcpy(new_endpage, my_endpage);
  }
  
  /* if either the startpage or the endpage contained leading slack, we have to add it back here */
  if (*endpage_leading_slack) {
    strcat(endpage_leading_slack, new_endpage);
    strcpy(new_endpage, endpage_leading_slack);
  }
  else if (*startpage_leading_slack) {
    strcat(startpage_leading_slack, new_endpage);
    strcpy(new_endpage, startpage_leading_slack);
  }

  /* try abbreviating numbers only if there is no leading slack. In the latter case we always use the full format */
  if (!(*endpage_leading_slack || *startpage_leading_slack)) {
    /* this format uses the minimum number of digits necessary to unambiguously denote the endpage */
    if (format && strcmp(format, "ABBREV") == 0) {
      strcpy(my_endpage, new_endpage);
      ptr_start = my_startpage;
      ptr_end = my_endpage;

      /* walk from left to right through startpage and expanded endpage until the digits differ */
      while (*ptr_end != '\0') {
	if (*ptr_start != *ptr_end) {
	  break;
	}
	ptr_start++;
	ptr_end++;
      }
      strcpy(new_endpage, ptr_end);
    }

    /* this format uses one (1-9) or two digits (> 10) or at least two digits (> 100) to render the endpage */
    else if (format && strcmp(format, "TWODIGIT") == 0) {
      if (strlen(new_endpage) > 2) {
	strcpy(my_endpage, new_endpage);
	ptr_start = my_startpage;
	ptr_end = my_endpage;
	ptr_end_end = my_endpage + strlen(my_endpage);
	
	/* take as many digits as are different between start and end, but take at least two digits */
	while (ptr_end_end - ptr_end > 2) {
	  if (*ptr_start != *ptr_end) {
	    break;
	  }
	  ptr_start++;
	  ptr_end++;
	}
	
	strcpy(new_endpage, ptr_end);
      }
      /* else: one or two-digit string is still in new_endpage, needs no modification */
    }

    /* ToDo: this format appears odd to me and needs some more research to find out about the rules. A brief web search made me feel it is best to use full citation style here. Only Reference Manager claims that Chicago style is different from full, but the examples given are inconsistent. */
    /*    else if (format && strcmp(format, "CHICAGO") == 0) { */
    /*    } */
    
    /* full means the endpage is rendered with at least as many digits as the startpage, nothing gets abbreviated. Nothing to do, expanded endpage is still in new_endpage */
    /*    if (format && strcmp(format, "FULL") == 0) { */
    /*    } */
  }
  /* else: with leading slack, assume "FULL" which means nothing to do */

  return strcat(new_endpage, endpage_trailing_slack);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_authorname(): converts a RIS author into a sequence of DocBook
                  or TEI elements

  static int format_authorname returns 0 if successful, >0 if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least one byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int containing the allocated length of res
                   will be updated accordingly if ref is reallocated

  const AUTHOR_INFO* ptr_ainfo ptr to struct with author name parts

  const char* authorsep ptr to a buffer containing the separator that
                   should appear right before the current author

  const char* nameorder ptr to a string containing the name order info

  const char* initialstyle ptr to a string containing the initials style

  const char* author_upper ptr to a string containing the upper/lowercase
                   info
 
  int nis_intext 1 if we format an in-text citation; 0 if bibliography

  int type 1=part author 2=publication author 3=series author 4=first available
           0=all

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  int* ptr_trailing_dot ptr to an int which will be set to non-zero if the
       last name part ended with a period

 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int format_authorname(char** ptr_ref, size_t* ptr_ref_len, struct AUTHOR_INFO* ptr_ainfo, const char* authorsep, const char* nameorder, const char* initialstyle, const char* author_upper, int nis_intext, int type, const char* ns, struct xmlindent* ptr_indent, int n_ref_format, int* ptr_trailing_dot) {
  char elname[16];
  char attname[16];
  char* new_ref;
  char* entitize_buf; /* buffer for replacing &<> with sgml entities */

/*    printf("in format_authorname; author:%s; authorsep:%s; nameorder:%s; initialstyle:%s; author_upper:%s; nis_intext:%d<<\n", author, authorsep, nameorder, initialstyle, author_upper, author_preceeding, author_following, nis_intext); */

  if (!ptr_ainfo) {
    return 234;
  }

  /* assemble string */
  if (authorsep && *authorsep) { /* have separator string */
    if ((entitize_buf = mstrdup((char*)authorsep)) == NULL) {
      return 801;
    }
    
    if (sgml_entitize(&entitize_buf, n_ref_format) == NULL) {
      free(entitize_buf);
      return 801;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, entitize_buf, ptr_ref_len, 0)) == NULL) {
      free(entitize_buf);
      return 801;
    }
    else {
      *ptr_ref = new_ref;
    }
    free(entitize_buf);
  }
  
  /* set wrapper element and attribute */
  switch (n_ref_format) {
/*   case REFDOCBKX5: */
/*     strcpy(elname, "personname"); */
/*     strcpy(attname, "role"); */
/*     break; */
  case REFTEIX5:
    strcpy(elname, "persName");
    strcpy(attname, "type");
    break;
  default:
    strcpy(elname, "bibliomset");
    strcpy(attname, "relation");
    break;
  }

  if (n_ref_format == REFRTF) {
    /* todo: does RTF need something here? */
  }
  else {
    /* wrap bibliomset around the name parts */
    switch (type) {
    case 1: /* fall through */
    case 4:
      if (print_elstart_x(ptr_ref, ptr_ref_len, elname, attname, "author", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	return 801;
      }
      break;
    case 2:
      if (print_elstart_x(ptr_ref, ptr_ref_len, elname, attname, "editor", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	return 801;
      }
      break;
    case 3:
      if (print_elstart_x(ptr_ref, ptr_ref_len, elname, attname, "seditor", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	return 801;
      }
      break;
    default:
      break;
    }
  }

  if (nameorder && strcmp(nameorder, "FIRSTMIDDLELAST") == 0) {
    if ((new_ref = format_firstmiddlename(ptr_ref, ptr_ref_len, ptr_ainfo, author_upper, initialstyle, nis_intext, type, ns, ptr_indent, n_ref_format, ptr_trailing_dot)) == NULL) {
      return 801;
    }
    else {
      *ptr_ref = new_ref;
    }

    if ((*(ptr_ainfo->firstname) || *(ptr_ainfo->middlename)) && (*(ptr_ainfo->lastname) || *(ptr_ainfo->name))) {
      if ((new_ref = mstrcat(*ptr_ref, " ", ptr_ref_len, 0)) == NULL) {
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
    }

    if ((new_ref = format_lastname(ptr_ref, ptr_ref_len, *(ptr_ainfo->lastname) ? ptr_ainfo->lastname : ptr_ainfo->name, author_upper, nis_intext, type, ns, ptr_indent, ptr_trailing_dot, n_ref_format)) == NULL) {
      return 801;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  else if (nameorder && (strcmp(nameorder, "LASTCOMMAFIRSTMIDDLE") == 0
			 || strcmp(nameorder, "LASTCOMMASPCFIRSTMIDDLE") == 0)) {
    char sep[3];

    /* quick and dirty strcmp */
    if (nameorder[9] == 'F') {
      strcpy(sep, ",");
    }
    else {
      strcpy(sep, ", ");
    }

    if ((new_ref = format_lastname(ptr_ref, ptr_ref_len, *(ptr_ainfo->lastname) ? ptr_ainfo->lastname : ptr_ainfo->name, author_upper, nis_intext, type, ns, ptr_indent, ptr_trailing_dot, n_ref_format)) == NULL) {
      return 801;
    }
    else {
      *ptr_ref = new_ref;
    }

    if ((*(ptr_ainfo->lastname) || *(ptr_ainfo->name)) && (*(ptr_ainfo->firstname) || *ptr_ainfo->middlename)) {
      if ((new_ref = mstrcat(*ptr_ref, sep, ptr_ref_len, 0)) == NULL) {
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
    }

    if ((new_ref = format_firstmiddlename(ptr_ref, ptr_ref_len, ptr_ainfo, author_upper, initialstyle, nis_intext, type, ns, ptr_indent, n_ref_format, ptr_trailing_dot)) == NULL) {
      return 801;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  else if (nameorder && strcmp(nameorder, "LASTFIRSTMIDDLE") == 0) {
    if ((new_ref = format_lastname(ptr_ref, ptr_ref_len, *(ptr_ainfo->lastname) ? ptr_ainfo->lastname : ptr_ainfo->name, author_upper, nis_intext, type, ns, ptr_indent, ptr_trailing_dot, n_ref_format)) == NULL) {
      return 801;
    }
    else {
      *ptr_ref = new_ref;
    }

    if ((*(ptr_ainfo->lastname) || *(ptr_ainfo->name)) && (*(ptr_ainfo->firstname) || *(ptr_ainfo->middlename))) {
      if ((new_ref = mstrcat(*ptr_ref, " ", ptr_ref_len, 0)) == NULL) {
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
    }

    if ((new_ref = format_firstmiddlename(ptr_ref, ptr_ref_len, ptr_ainfo, author_upper, initialstyle, nis_intext, type, ns, ptr_indent, n_ref_format, ptr_trailing_dot)) == NULL) {
      return 801;
    }
    else {
      *ptr_ref = new_ref;
    }
  }
  else if (nameorder && strcmp(nameorder, "LAST") == 0) {
    if ((new_ref = format_lastname(ptr_ref, ptr_ref_len, *(ptr_ainfo->lastname) ? ptr_ainfo->lastname : ptr_ainfo->name, author_upper, nis_intext, type, ns, ptr_indent, ptr_trailing_dot, n_ref_format)) == NULL) {
      return 801;
    }
    else {
      *ptr_ref = new_ref;
    }
  }

  if (*(ptr_ainfo->suffix)) {
    if ((entitize_buf = mstrdup(ptr_ainfo->suffix)) == NULL) {
      return 801;
    }
    
    if (sgml_entitize(&entitize_buf, n_ref_format) == NULL) {
      free(entitize_buf);
      return 801;
    }
    
    if (n_ref_format == REFTEIX5) {
      if (print_element_x(entitize_buf, ptr_ref, ptr_ref_len, "name", "type", "lineage", NULL, NULL, ptr_indent, ns) == NULL) {
	free(entitize_buf);
	return 801;
      }
    }
    else if (n_ref_format == REFRTF) {
      if (print_rtf_string(entitize_buf, ptr_ref, ptr_ref_len) == NULL) {
	free(entitize_buf);
	return 801;
      }
    }
    else if (n_ref_format == REFDOCBKX5) {
      /* insert a space before the lineage or honorific */
      if ((new_ref = mstrcat(*ptr_ref, " ", ptr_ref_len, 0)) == NULL) {
	free(entitize_buf);
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
      if ((new_ref = mstrcat(*ptr_ref, entitize_buf, ptr_ref_len, 0)) == NULL) {
	free(entitize_buf);
	return 801;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    else {
      if (print_element_x(entitize_buf, ptr_ref, ptr_ref_len, "lineage", NULL, NULL, NULL, NULL, ptr_indent, ns) == NULL) {
	free(entitize_buf);
	return 801;
      }
    }
    free(entitize_buf);
  }

  if (n_ref_format == REFRTF) {
    /* todo: does RTF need something here? */
  }
  /* end bibliomset wrap around the name parts */
  else if (print_elend_x(ptr_ref, ptr_ref_len, elname, ptr_indent, ns) == NULL) {
    return 801;
  }

/*    printf("done format_authorname\n"); */
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_lastname(): appends a surname as a docbook element to a string

  static char* format_lastname returns a pointer to the modified string

  char* format_lastname returns ptr to the buffer if successful, NULL if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least one byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to length of *ptr_ref

  char* lastname ptr to a string containing the lastname

  const char* author_upper ptr to a string containing the upper/lowercase
                   information

  int nis_intext 1 if we format an in-text citation; 0 if bibliography

  int type 1=part author 2=publication author 3=series author 4=first
           available 0=all

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int* ptr_trailing_dot ptr to int which will be set to nonzero if the
         lastname ended with a period

  int n_ref_format REFXX output type

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* format_lastname(char** ptr_ref, size_t* ptr_ref_len, char* lastname, const char* author_upper, int nis_intext, int type, const char* ns, struct xmlindent* ptr_indent, int* ptr_trailing_dot, int n_ref_format) {
  char* entitize_buf; /* buffer for replacing &<> with sgml entities */
  char* new_ref;
  
  if (lastname && *lastname) {
    /* reset trailing dot indicator */
    *ptr_trailing_dot = 0;

    if (lastname[strlen(lastname)-1] == '.') {
      *ptr_trailing_dot++;
    }

    if (author_upper && (strcmp(author_upper, "LASTNAME") == 0 || strcmp(author_upper, "ALL") == 0)) {
      strup(lastname);
    }

    if ((entitize_buf = mstrdup(lastname)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return NULL;
    }
    
    if (sgml_entitize(&entitize_buf, n_ref_format) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(entitize_buf);
      return NULL;
    }
      
    if (n_ref_format == REFDOCBKX5) {
      if ((new_ref = mstrcat(*ptr_ref, entitize_buf, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(entitize_buf);
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
    }
    else if (n_ref_format == REFRTF) {
      if (print_rtf_string(entitize_buf, ptr_ref, ptr_ref_len) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(entitize_buf);
	return NULL;
      }
    }
    else {
      if (print_element_x(entitize_buf, ptr_ref, ptr_ref_len, "surname", NULL, NULL, NULL, NULL, ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(entitize_buf);
	return NULL;
      }
    }
  }
  return *ptr_ref;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  format_firstmiddlename(): appends a firstname/middlename combo as a
                   docbook element to a string

  static char* format_firstmiddlename returns ptr to the buffer if successful, NULL if failed

  char** ptr_ref ptr to a ptr to a buffer created with malloc which will receive
        the output. The calling function must allocate the buffer
        with at least one byte. This function will reallocate the
        buffer as needed. *ptr will be updated whenever a realloc is
        necessary. The calling function is responsible for freeing the
        memory again.

  size_t* ptr_ref_len ptr to an int containing the allocated length of res
                   will be updated accordingly if ref is reallocated

  struct AUTHOR_INFO* ptr_ainfo ptr to struct with name parts

  const char* author_upper ptr to a string containing the upper/lowercase
                   information

  const char* initialstyle ptr to a string containing the initial style
                   information

  int nis_intext 1 if we format an in-text citation; 0 if bibliography

  int type 1=part author 2=publication author 3=series author 4=first
           available 0=all

  const char* ns optional namespace prefix

  struct xmlindent* ptr_indent  indentation information

  int n_ref_format requested output format: REFDOCBK, REFDOCBKX, REFTEIX

  int* ptr_trailing_dot ptr to int which will be set to nonzero if the
         last name component ended with a period

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* format_firstmiddlename(char** ptr_ref, size_t* ptr_ref_len, struct AUTHOR_INFO* ptr_ainfo, const char* author_upper, const char* initialstyle, int nis_intext, int type, const char* ns, struct xmlindent* ptr_indent, int n_ref_format, int* ptr_trailing_dot) {
  char* new_ref;
  char* entitize_buf; /* buffer for replacing &<> with sgml entities */

  if (*(ptr_ainfo->firstname)) {
    /* reset trailing dot indicator */
    *ptr_trailing_dot = 0;

    if (author_upper && strcmp(author_upper, "ALL") == 0) {
      strup(ptr_ainfo->firstname);
    }

    if (n_ref_format == REFTEIX5) {
      if (print_elstart_x(ptr_ref, ptr_ref_len, "forename", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
      }
    }
    else if (n_ref_format == REFRTF) {
      /* todo: insert RTF stuff */
    }
    else if (n_ref_format != REFDOCBKX5) {
      if (print_elstart_x(ptr_ref, ptr_ref_len, "firstname", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
      }
    }

    if ((entitize_buf = mstrdup(ptr_ainfo->firstname)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      return NULL;
    }
    
    /* see whether we need the initial or the full name */
    if (initialstyle && (strcmp(initialstyle, "F.SPCM.") == 0 ||
			 strcmp(initialstyle, "F.M.") == 0 ||
			 strcmp(initialstyle, "FM") == 0 ||
			 strcmp(initialstyle, "FSPCM") == 0)) {
      if (strlen(entitize_buf) > 0) {
	*(entitize_buf + 1) = '\0';
      }
    }

    if (sgml_entitize(&entitize_buf, n_ref_format) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(entitize_buf);
      return NULL;
    }
      
    if ((new_ref = mstrcat(*ptr_ref, entitize_buf, ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(entitize_buf);
      return NULL;
    }
    else {
      *ptr_ref = new_ref;
    }
    free(entitize_buf);

    if (initialstyle && (strcmp(initialstyle, "F.SPCM.") == 0 ||
			 strcmp(initialstyle, "F.M.") == 0)) {
      *ptr_trailing_dot = 1;
      if ((new_ref = mstrcat(*ptr_ref, ".", ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
    }

    if (n_ref_format == REFTEIX5) {
      if (print_elend_x(ptr_ref, ptr_ref_len, "forename", ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
      }
    }
    else if (n_ref_format == REFRTF) {
      /* todo: insert RTF stuff */
    }
    else if (n_ref_format != REFDOCBKX5) {
      if (print_elend_x(ptr_ref, ptr_ref_len, "firstname", ptr_indent, ns) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
      }
    }
  }

  if (*(ptr_ainfo->middlename)) {
    char* item;

    /* reset trailing dot indicator */
    *ptr_trailing_dot = 0;

    for (item = strtok(ptr_ainfo->middlename, " "); item; item = strtok(NULL, " ")) {
      if (initialstyle && (strcmp(initialstyle, "F.SPCM.") == 0 ||
			   strcmp(initialstyle, "FIRSTSPCMIDDLE") == 0 ||
			   strcmp(initialstyle, "FIRSTSPCM.") == 0 ||
			   strcmp(initialstyle, "FIRSTSPCM") == 0 ||
			   strcmp(initialstyle, "FSPCM") == 0)) {
	if ((new_ref = mstrcat(*ptr_ref, " ", ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }
      
      if (n_ref_format == REFTEIX5) {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "name", "type", "othername", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  return NULL;
	}
      }
      else if (n_ref_format == REFRTF) {
	/* todo: insert RTF stuff */
      }
      else if (n_ref_format != REFDOCBKX5) {
	if (print_elstart_x(ptr_ref, ptr_ref_len, "othername", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 /* not empty */, ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  return NULL;
	}
      }

      if ((entitize_buf = mstrdup(item)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	return NULL;
      }
    
      if (author_upper && strcmp(author_upper, "ALL") == 0) {
	strup(entitize_buf);
      }

      /* see whether we need the initial or the full name */
      if (initialstyle && (strcmp(initialstyle, "F.SPCM.") == 0 ||
			   strcmp(initialstyle, "F.M.") == 0 ||
			   strcmp(initialstyle, "FM") == 0 ||
			   strcmp(initialstyle, "FIRSTSPCM.") == 0 ||
			   strcmp(initialstyle, "FIRSTSPCM") == 0 ||
			   strcmp(initialstyle, "FSPCM") == 0)) {
	if (strlen(entitize_buf) > 0) {
	  *(entitize_buf + 1) = '\0';
	}
      }

      if (sgml_entitize(&entitize_buf, n_ref_format) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(entitize_buf);
	return NULL;
      }
      
      if ((new_ref = mstrcat(*ptr_ref, entitize_buf, ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(entitize_buf);
	return NULL;
      }
      else {
	*ptr_ref = new_ref;
      }
      free(entitize_buf);

      if (initialstyle && (strcmp(initialstyle, "F.SPCM.") == 0 ||
			   strcmp(initialstyle, "F.M.") == 0 ||
			   strcmp(initialstyle, "FIRSTSPCM.") == 0)) {
	*ptr_trailing_dot = 1;
	if ((new_ref = mstrcat(*ptr_ref, ".", ptr_ref_len, 0)) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  return NULL;
	}
	else {
	  *ptr_ref = new_ref;
	}
      }

      if (n_ref_format == REFTEIX5) {
	if (print_elend_x(ptr_ref, ptr_ref_len, "name", ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  return NULL;
	}
      }
      else if (n_ref_format == REFRTF) {
	/* todo: insert RTF stuff */
      }
      else if (n_ref_format != REFDOCBKX5) {
	if (print_elend_x(ptr_ref, ptr_ref_len, "othername", ptr_indent, ns) == NULL) {
	  LOG_PRINT(LOG_WARNING, get_status_msg(801));
	  return NULL;
	}
      }
    } /* end for */
  } /* end if have middlename */
  return *ptr_ref;
}

