/* xmlhandler.c: functions to write bibliography style sets to a database */
/* markus@mhoenicka.de 12-29-00 */
/* $Id: xmlhandler.c,v 1.22.2.25 2006/02/22 21:02:45 mhoenicka Exp $ */

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

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

/* ToDo: figure out a way to handle out of memory situations properly while parsing a style set. It is not yet proven that the current implementation frees all memory that has been allocated prior to the memory error */

/* Overview */
/* This set of functions parses a XML file containing bibliography style sets according to the CiteStyle DTD. We use expat as a non-validating XML parser. We register three handlers for start tags, character data, and end tags. The elements are pushed on a stack in the start tags handler. Each structure defining an element contains a start element of another stack for the attributes of this element. These stacks are used in the character data handler and the end tag handler to retrieve parent and ancestor elements and attributes of the current element where necessary. The attribute stack of the current element is freed in the end tag handler and the current element is popped off the stack as well. */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <expat.h> /* header of the XML parser */
#include <syslog.h> /* priority levels of log messages */
#include <dbi/dbi.h>

#include "backend.h"
#include "linklist.h"
#include "refdb.h"
#include "refdbd.h" /* depends on backend.h */
#include "xmlhandler.h"
#include "strfncs.h"
#include "connect.h"
#include "dbfncs.h"

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

#ifndef HAVE_STRTOLL
long long strtoll(const char *nptr, char **endptr, int base);
#endif

extern int n_log_level;
extern char main_db[];

/* forward declarations of local functions */
static char* concat_elnames(struct elstack* ptr_current_element, char* exclude_name, char* concat);
static char* get_ancestor_attr(struct elstack* ptr_current_element, char* ancestor_name, char* ancestor_attribute);
static int set_alternatetext(dbi_result dbires, char** ptr_aempty);


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  start_handler(): handler for start tags (handling citestyle data)
  
  void start_handler has no return value

  void* ptr_data this is a ptr to "non-global" global data that all
             handlers share - will be cast to type struct addstyle_data*

  const char *el ptr to a string containing the element name

  const char** ptr_attr ptr to an array of attributes (name-value pairs)

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void start_handler(void *ptr_data, const char *el, const char **ptr_attr) {
  int i;
  char sql_command[256];
  const char *drivername; /* name of the libdbi driver */
  struct elstack* ptr_el_new;
  struct attrlist* ptr_attr_new;
  struct addstyle_data* ptr_asdata;
  dbi_result dbires;

/*    printf("el:attr0:attr1:%s:%s:%s<<\n", el, attr[0], attr[1]);  */

  ptr_asdata = (struct addstyle_data*)ptr_data;

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

  if (strcmp(el, "STYLESET") == 0) {
    (*(ptr_asdata->ptr_depth_adjust))++;
  }
  else if (!*(ptr_asdata->ptr_ndb_error) && !*(ptr_asdata->ptr_nmem_error)) {
    if (strcmp(el, "CITESTYLE") == 0) {
      /* create new entry in database */
      dbires = dbi_conn_query(ptr_asdata->conn, "INSERT INTO CITSTYLE (JOURNAL) VALUES (\'DUMMY\')");
      LOG_PRINT(LOG_DEBUG, "INSERT INTO CITSTYLE (JOURNAL) VALUES (\'DUMMY\')");
      if (!dbires) {
	(*(ptr_asdata->ptr_ndb_error))++;
	LOG_PRINT(LOG_WARNING, "error in sql command");
	return;
      }
      else {
	if (!strcmp(my_dbi_conn_get_cap(ptr_asdata->conn, "named_seq"), "f")) {
	  *(ptr_asdata->ptr_citstyle_id) = dbi_conn_sequence_last(ptr_asdata->conn, NULL);
	}
	else {
	  *(ptr_asdata->ptr_citstyle_id) = dbi_conn_sequence_last(ptr_asdata->conn, "citstyle_id_seq");
	}
	dbi_result_free(dbires);
      }
    }
    else if (strcmp(el, "PUBTYPE") == 0 ||
	     strcmp(el, "INTEXTDEF") == 0 ||
	     strcmp(el, "AUTHORONLY") == 0 ||
	     strcmp(el, "YEARONLY") == 0) {
      /* reset counter */
      *(ptr_asdata->ptr_position) = 0;
      sprintf(sql_command, "INSERT INTO REFSTYLE (CITSTYLEID, PUBTYPE) VALUES (%u"", '", *(ptr_asdata->ptr_citstyle_id));

      if (strcmp(el, "PUBTYPE") == 0) {
	/* create new entry in database. PUBTYPE has only one attribute TYPE, the value of which is therefore in attr[1], if at all */
	/* NB ptr_attr == NULL is not permitted by the DTD but
	   we better check */
	if (ptr_attr && ptr_attr[1] && *(ptr_attr[1])) {
	  strcat(sql_command, ptr_attr[1]);
	}
	else {
	  strcat(sql_command, "GEN"); /* default as per DTD */
	}
      }
      else if (strcmp(el, "INTEXTDEF") == 0) {
	/* create new entry in database. INTEXTDEF is treated like a PUBTYPE element with the attribute INTEXT */
	strcat(sql_command, "INTEXT");
      }
      else if (strcmp(el, "AUTHORONLY") == 0) {
	/* create new entry in database. AUTHORONLY is treated like a PUBTYPE element with the attribute AUTHORONLY */
	strcat(sql_command, "AUTHORONLY");
      }
      else { /* yearonly */
	/* create new entry in database. YEARONLY is treated like a PUBTYPE element with the attribute YEARONLY */
	strcat(sql_command, "YEARONLY");
      }
      strcat(sql_command, "\')");
      /*      printf("%s\n", sql_command); */
      dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	(*(ptr_asdata->ptr_ndb_error))++;
	LOG_PRINT(LOG_WARNING, "error in sql command");
      }
      else {
	if (!strcmp(my_dbi_conn_get_cap(ptr_asdata->conn, "named_seq"), "f")) {
	  *(ptr_asdata->ptr_refstyle_id) = dbi_conn_sequence_last(ptr_asdata->conn, NULL);
	}
	else {
	  *(ptr_asdata->ptr_refstyle_id) = dbi_conn_sequence_last(ptr_asdata->conn, "refstyle_id_seq");
	}
	dbi_result_free(dbires);
      }
    }
    else if (strcmp(el, "SEPARATOR") == 0) {
      sprintf(sql_command, "INSERT INTO SEPARATORS (VALUE) VALUES ('Dummy')");
      
      dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	(*(ptr_asdata->ptr_ndb_error))++;
	LOG_PRINT(LOG_WARNING, "error in sql command");
      }
      else {
	if (!strcmp(my_dbi_conn_get_cap(ptr_asdata->conn, "named_seq"), "f")) {
	  *(ptr_asdata->ptr_separator_id) = dbi_conn_sequence_last(ptr_asdata->conn, NULL);
	}
	else {
	  *(ptr_asdata->ptr_separator_id) = dbi_conn_sequence_last(ptr_asdata->conn, "separators_id_seq");
	}
	dbi_result_free(dbires);
      }
    }
  }
  /*    printf("add to stack\n"); */
  /* add current element to element stack */
  ptr_el_new = malloc(sizeof(struct elstack));
  if (ptr_el_new == NULL) {
    (*(ptr_asdata->ptr_nmem_error))++;
  }
  else {
    /*    printf("have memory\n"); */
    strncpy(ptr_el_new->elname, el, 63);
    ptr_el_new->elname[63] = '\0'; /* terminate just in case */
    ptr_el_new->n_elvalue_len = ELVALUE_LENGTH;
    ptr_el_new->ptr_elvalue = malloc(ELVALUE_LENGTH);
    if (ptr_el_new->ptr_elvalue == NULL) {
      (*(ptr_asdata->ptr_nmem_error))++;
    }
    else {
      *(ptr_el_new->ptr_elvalue) = '\0';
    }
    ptr_el_new->ptr_next = ptr_asdata->ptr_first;
    ptr_el_new->ptr_attr_first = NULL;
    ptr_asdata->ptr_first = ptr_el_new;
    /*    printf("%s", ptr_first->elname); */

    /* add current attributes to the element */
    /*    printf("add attributes\n"); */
    for (i = 0; ptr_attr[i]; i += 2) {
      ptr_attr_new = malloc(sizeof(struct attrlist));
      if (ptr_attr_new == NULL) {
	(*(ptr_asdata->ptr_nmem_error))++;
	break;
      }
      strncpy(ptr_attr_new->attribute_name, ptr_attr[i], 63);
      ptr_attr_new->attribute_name[63] = '\0';
      strncpy(ptr_attr_new->attribute_value, ptr_attr[i+1], 63);
      ptr_attr_new->attribute_value[63] = '\0';
      ptr_attr_new->ptr_next = (ptr_asdata->ptr_first)->ptr_attr_first;
      (ptr_asdata->ptr_first)->ptr_attr_first = ptr_attr_new;
/*        printf(" %s='%s'", attr[i], attr[i + 1]); */
    }
    /*    printf("done adding attributes\n"); */
    (*(ptr_asdata->ptr_depth))++;
  }
}  /* End of start_handler */

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  end_handler(): handler for end tags (handling citestyle data)

  void end_handler has no return value

  void* ptr_data this is a ptr to "non-global" global data that all
             handlers share - will be cast to type struct addstyle_data*

  const char *el ptr to a string containing the element name

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void end_handler(void *ptr_data, const char *el) {
  int n_chardata = 0;
  int n_attrdata = 0;
  int ignore_role = 0;
  unsigned int current_id = 0;
  unsigned int n_id;
  char column_name[65];
  char table_name[65];
  char column_name_attribute[65];
  char pubtype[32];
  char sql_command[512];
  char concat[65];
  char* ptr_attr = NULL;
  char* new_msg;
  struct elstack* ptr_el_remove;
  struct attrlist* ptr_attr_remove;
  struct addstyle_data* ptr_asdata;
  dbi_result dbires;

  ptr_asdata = (struct addstyle_data*)ptr_data;

  (*(ptr_asdata->ptr_depth))--;
  pubtype[0] = '\0';
  column_name[0] = '\0';
  table_name[0] = '\0';

  if (!*(ptr_asdata->ptr_ndb_error) && !*(ptr_asdata->ptr_nmem_error)) {
/*    printf("start of end tag handler:%s depth: %d depth_adjust: %d\n", el, *(ptr_asdata->ptr_depth), *(ptr_asdata->ptr_depth_adjust));  */

    /* we have to update POSXY for these elements */
    if (strcmp((ptr_asdata->ptr_first)->elname, "SEPARATOR") == 0) {
      /* encode the id of the separator in the value of the POSXY field */
      sprintf(sql_command, "INSERT INTO POSITIONS (POS,TYPE,SEPARATORID,REFSTYLEID) VALUES (%d,'SEPARATOR',%u,%u)", *(ptr_asdata->ptr_position), *(ptr_asdata->ptr_separator_id), *(ptr_asdata->ptr_refstyle_id));

      dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	(*(ptr_asdata->ptr_ndb_error))++;
	LOG_PRINT(LOG_WARNING, "error in sql command");
      }
      else {
	/* increment position */
	(*(ptr_asdata->ptr_position))++;
	dbi_result_free(dbires);
      }
    }


    /* simple elements in REFTYPE, INTEXTDEF, AUTHORONLY, or YEARONLY */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "REFNUMBER") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "CITEKEY") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "JOURNALNAME") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "VOLUME") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "ISSUE") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "PAGES") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "PUBLISHER") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "PUBPLACE") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "SERIAL") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "ADDRESS") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "NOTES") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "ABSTRACT") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "TYPEOFWORK") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "AREA") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "OSTYPE") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "DEGREE") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "RUNNINGTIME") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "CLASSCODEINTL") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "CLASSCODEUS") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "SENDEREMAIL") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "RECIPIENTEMAIL") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "MEDIATYPE") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "NUMVOLUMES") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "EDITION") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "COMPUTER") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "CONFERENCELOCATION") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "REGISTRYNUM") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "CLASSIFICATION") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "SECTION") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "PAMPHLETNUM") == 0
	     ||	strcmp((ptr_asdata->ptr_first)->elname, "CHAPTERNUM") == 0
) {
	/* write element name in POSITIONS */
      sprintf(sql_command, "INSERT INTO POSITIONS (POS,TYPE,SEPARATORID,REFSTYLEID) VALUES (%d, '%s', 0, %u)", *(ptr_asdata->ptr_position), (ptr_asdata->ptr_first)->elname, *(ptr_asdata->ptr_refstyle_id));
      dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	(*(ptr_asdata->ptr_ndb_error))++;
	LOG_PRINT(LOG_WARNING, "error in sql command");
      }
      else {
	/* increment position */
	(*(ptr_asdata->ptr_position))++;
	dbi_result_free(dbires);
      }
    }


    /* AUTHORLIST, PUBDATE, TITLE, USERDEF, MISC, LINK need special care
       as the actual column name in the database depends on the ROLE
       attribute */

    /* first authorlist */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "AUTHORLIST") == 0) {
      ptr_attr = get_attr(ptr_asdata->ptr_first, "ROLE");
      if (!ptr_attr || strcmp(ptr_attr, "PART") == 0) {
	strcpy(concat, "AUTHORLIST");
      }
      else if (strcmp(ptr_attr, "PUB") == 0) {
	strcpy(concat, "EDITORLIST");
      }
      else if (strcmp(ptr_attr, "SERIES") == 0) {
	strcpy(concat, "SEDITORLIST");
      }
      else if (strcmp(ptr_attr, "ALL") == 0) {
	strcpy(concat, "ALLALIST");
      }

	/* write element name in POSITIONS */
      sprintf(sql_command, "INSERT INTO POSITIONS (POS,TYPE,SEPARATORID,REFSTYLEID) VALUES (%d, '%s', 0, %u)", *(ptr_asdata->ptr_position), concat, *(ptr_asdata->ptr_refstyle_id));
      dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	(*(ptr_asdata->ptr_ndb_error))++;
	LOG_PRINT(LOG_WARNING, "error in sql command");
      }
      else {
	/* increment position */
	(*(ptr_asdata->ptr_position))++;
	dbi_result_free(dbires);
      }
    }

    /* second pubdate */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "PUBDATE") == 0) {
      ptr_attr = get_attr(ptr_asdata->ptr_first, "ROLE");
      if (!ptr_attr || strcmp(ptr_attr, "PRIMARY") == 0) {
	strcpy(concat, "PUBDATE");
      }
      else if (strcmp(ptr_attr, "SECONDARY") == 0) {
	strcpy(concat, "PUBDATESEC");
      }
      else if (strcmp(ptr_attr, "ALL") == 0) {
	strcpy(concat, "PUBDATEALL");
      }

	/* write element name in POSITIONS */
      sprintf(sql_command, "INSERT INTO POSITIONS (POS,TYPE,SEPARATORID,REFSTYLEID) VALUES (%d, '%s', 0, %u)", *(ptr_asdata->ptr_position), concat, *(ptr_asdata->ptr_refstyle_id));
      dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	(*(ptr_asdata->ptr_ndb_error))++;
	LOG_PRINT(LOG_WARNING, "error in sql command");
      }
      else {
	/* increment position */
	(*(ptr_asdata->ptr_position))++;
	dbi_result_free(dbires);
      }
    }

    /* third title */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "TITLE") == 0) {
      ptr_attr = get_attr(ptr_asdata->ptr_first, "ROLE");
      if (!ptr_attr || strcmp(ptr_attr, "PART") == 0) {
	strcpy(concat, "TITLE");
      }
      else if (strcmp(ptr_attr, "PUB") == 0) {
	strcpy(concat, "BOOKTITLE");
      }
      else if (strcmp(ptr_attr, "SERIES") == 0) {
	strcpy(concat, "SERIESTITLE");
      }
      else if (strcmp(ptr_attr, "ALL") == 0) {
	strcpy(concat, "ALLTITLE");
      }

	/* write element name in POSITIONS */
      sprintf(sql_command, "INSERT INTO POSITIONS (POS,TYPE,SEPARATORID,REFSTYLEID) VALUES (%d, '%s', 0, %u)", *(ptr_asdata->ptr_position), concat, *(ptr_asdata->ptr_refstyle_id));
      dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	(*(ptr_asdata->ptr_ndb_error))++;
	LOG_PRINT(LOG_WARNING, "error in sql command");
      }
      else {
	/* increment position */
	(*(ptr_asdata->ptr_position))++;
	dbi_result_free(dbires);
      }
    }
    else if (strcmp((ptr_asdata->ptr_first)->elname, "USERDEF") == 0) {
      ptr_attr = get_attr(ptr_asdata->ptr_first, "ROLE");
      if (!ptr_attr || strcmp(ptr_attr, "1") == 0) {
	strcpy(concat, "USERDEF1");
      }
      else if (strcmp(ptr_attr, "2") == 0) {
	strcpy(concat, "USERDEF2");
      }
      else if (strcmp(ptr_attr, "3") == 0) {
	strcpy(concat, "USERDEF3");
      }
      else if (strcmp(ptr_attr, "4") == 0) {
	strcpy(concat, "USERDEF4");
      }
      else if (strcmp(ptr_attr, "5") == 0) {
	strcpy(concat, "USERDEF5");
      }

	/* write element name in POSITIONS */
      sprintf(sql_command, "INSERT INTO POSITIONS (POS,TYPE,SEPARATORID,REFSTYLEID) VALUES (%d, '%s', 0, %u)", *(ptr_asdata->ptr_position), concat, *(ptr_asdata->ptr_refstyle_id));
      dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	(*(ptr_asdata->ptr_ndb_error))++;
	LOG_PRINT(LOG_WARNING, "error in sql command");
      }
      else {
	/* increment position */
	(*(ptr_asdata->ptr_position))++;
	dbi_result_free(dbires);
      }
    }
    else if (strcmp((ptr_asdata->ptr_first)->elname, "LINK") == 0
	     || strcmp((ptr_asdata->ptr_first)->elname, "URL") == 0) {
      if (*((ptr_asdata->ptr_first)->elname) == 'L') {
	ptr_attr = get_attr(ptr_asdata->ptr_first, "ROLE");
      }

      if (*((ptr_asdata->ptr_first)->elname) == 'U'
	  || ptr_attr == NULL /* 0 is implied default */
	  || strcmp(ptr_attr, "0") == 0) {
	strcpy(concat, "LINK0");
      }
      else if (strcmp(ptr_attr, "1") == 0) {
	strcpy(concat, "LINK1");
      }
      else if (strcmp(ptr_attr, "2") == 0) {
	strcpy(concat, "LINK2");
      }
      else if (strcmp(ptr_attr, "3") == 0) {
	strcpy(concat, "LINK3");
      }
      else if (strcmp(ptr_attr, "4") == 0) {
	strcpy(concat, "LINK4");
      }

	/* write element name in POSITIONS */
      sprintf(sql_command, "INSERT INTO POSITIONS (POS,TYPE,SEPARATORID,REFSTYLEID) VALUES (%d, '%s', 0, %u)", *(ptr_asdata->ptr_position), concat, *(ptr_asdata->ptr_refstyle_id));
      dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	(*(ptr_asdata->ptr_ndb_error))++;
	LOG_PRINT(LOG_WARNING, "error in sql command");
      }
      else {
	/* increment position */
	(*(ptr_asdata->ptr_position))++;
	dbi_result_free(dbires);
      }
    }

    /* set ignore_role to 1 for those elements that treat the ROLE attribute in a special way (the attribute is used to translate the element name to a different column name than expected) */
    if (strcmp((ptr_asdata->ptr_first)->elname, "AUTHORLIST") == 0 ||
	strcmp((ptr_asdata->ptr_first)->elname, "PUBDATE") == 0 ||
	strcmp((ptr_asdata->ptr_first)->elname, "TITLE") == 0 ||
	strcmp((ptr_asdata->ptr_first)->elname, "LINK") == 0 ||
	strcmp((ptr_asdata->ptr_first)->elname, "USERDEF") == 0) {
      ignore_role = 1;
    }

    /* the name of the style and related style info */
    if (strcmp((ptr_asdata->ptr_first)->elname, "STYLENAME") == 0) {
      char* the_item_q = NULL;

      if ((the_item_q = strdup((ptr_asdata->ptr_first)->ptr_elvalue)) == NULL) {
	(*(ptr_asdata->ptr_nmem_error))++;
	LOG_PRINT(LOG_CRIT, get_status_msg(801));
      }
      else {
	if (dbi_conn_quote_string(ptr_asdata->conn, &the_item_q) == 0) {
	  (*(ptr_asdata->ptr_nmem_error))++;
	  LOG_PRINT(LOG_CRIT, get_status_msg(801));
	}
      }

      if (the_item_q) {
	strncpy(ptr_asdata->the_journal, the_item_q, 255);
	ptr_asdata->the_journal[255] = '\0';
	free(the_item_q);
      }
      else {
	ptr_asdata->the_journal[0] = '\0';
      }

      strcpy(column_name, "JOURNAL");
      strcpy(table_name, "CITSTYLE");
      n_chardata = 1;
      current_id = *(ptr_asdata->ptr_citstyle_id);
    }

    if (strcmp((ptr_asdata->ptr_first)->elname, "AUTHOR") == 0
	|| strcmp((ptr_asdata->ptr_first)->elname, "COMMENT") == 0
	|| strcmp((ptr_asdata->ptr_first)->elname, "URL") == 0) {
      strcpy(column_name, (ptr_asdata->ptr_first)->elname);
      strcpy(table_name, "CITSTYLE");
      n_chardata = 1;
      current_id = *(ptr_asdata->ptr_citstyle_id);
    }

    /* elements with character data in PUBTYPE, INTEXT, AUTHORONLY, or YEARONLY */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "ABBREVIATEFIRST") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "ABBREVIATESUBSEQ") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "AEMPTY") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "ASAME") == 0) {
      /* assemble column name */
      if (!strcmp(((ptr_asdata->ptr_first)->ptr_next)->elname, "AUTHORLIST")) {
	/* check role attribute of ancestor */
	ptr_attr = get_ancestor_attr(ptr_asdata->ptr_first, "AUTHORLIST", "ROLE");
	if (ptr_attr && strcmp(ptr_attr, "PART") == 0) {
	  strcpy(column_name, "Q"); /* AUTHORLIST */
	}
	else if (ptr_attr && strcmp(ptr_attr, "SERIES") == 0) {
	  strcpy(column_name, "Y"); /* SEDITORLIST */
	}
	else if (ptr_attr && strcmp(ptr_attr, "ALL") == 0) {
	  strcpy(column_name, "Z"); /* ALLALIST */
	}
	else {
	  /* DTD uses this as default */
	  strcpy(column_name, "X"); /* EDITORLIST */
	}
      }
      else {
	strcpy(column_name, ((ptr_asdata->ptr_first)->ptr_next)->elname);
      }
      strcat(column_name, (ptr_asdata->ptr_first)->elname);

      /* we expect char data and attr data */
      n_chardata = 1;
      n_attrdata = 1;
      current_id = *(ptr_asdata->ptr_citstyle_id);
      strcpy(table_name, "REFSTYLE");

      if (is_descendant_of(ptr_asdata->ptr_first, "PUBTYPE")) {
	strcpy(pubtype, get_ancestor_attr(ptr_asdata->ptr_first, "PUBTYPE", "TYPE"));
      }
      else if (is_descendant_of(ptr_asdata->ptr_first, "INTEXTDEF")) {
	strcpy(pubtype, "INTEXT");
      }
      else if (is_descendant_of(ptr_asdata->ptr_first, "AUTHORONLY")) {
	strcpy(pubtype, "AUTHORONLY");
      }
      else { /* yearonly */
	strcpy(pubtype, "YEARONLY");
      }
    }

    /* elements with character data in CITSTYLE */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "CITSEPARATOR") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "BIBLIOTITLE") == 0) {
      strcpy(table_name, "CITSTYLE");
      strcpy(column_name, (ptr_asdata->ptr_first)->elname);
      n_chardata = 1;
      current_id = *(ptr_asdata->ptr_citstyle_id);
    }

    /* elements with character data at various locations */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "RANGESEPARATOR") == 0) {
      if (is_descendant_of(ptr_asdata->ptr_first, "PUBTYPE")) {
	strcpy(table_name, "REFSTYLE");
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "PUBTYPE", concat));
	strcpy(pubtype, get_ancestor_attr(ptr_asdata->ptr_first, "PUBTYPE", "TYPE"));
	n_chardata = 1;
	n_attrdata = 1;
	current_id = *(ptr_asdata->ptr_citstyle_id);
      }
      else if (is_descendant_of(ptr_asdata->ptr_first, "INTEXTDEF")) {
	strcpy(table_name, "REFSTYLE");
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "INTEXTDEF", concat));
	strcpy(pubtype, "INTEXT");
	n_chardata = 1;
	n_attrdata = 1;
	current_id = *(ptr_asdata->ptr_citstyle_id);
      }
      else if (is_descendant_of(ptr_asdata->ptr_first, "AUTHORONLY")) {
	strcpy(table_name, "REFSTYLE");
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "AUTHORONLY", concat));
	strcpy(pubtype, "AUTHORONLY");
	n_chardata = 1;
	n_attrdata = 1;
	current_id = *(ptr_asdata->ptr_citstyle_id);
      }
      else if (is_descendant_of(ptr_asdata->ptr_first, "YEARONLY")) {
	strcpy(table_name, "REFSTYLE");
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "YEARONLY", concat));
	strcpy(pubtype, "YEARONLY");
	n_chardata = 1;
	n_attrdata = 1;
	current_id = *(ptr_asdata->ptr_citstyle_id);
      }
      else { /* CITSTYLE */
	strcpy(table_name, "CITSTYLE");
	strcpy(column_name, (ptr_asdata->ptr_first)->elname);
	n_chardata = 1;
	current_id = *(ptr_asdata->ptr_citstyle_id);
      }
    }

    /* can be level 2, 3, 4, 5, 6 */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "FOLLOWING") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "PRECEEDING") == 0) {

/*        printf("depth:%d\n", depth); */
      if (*(ptr_asdata->ptr_depth) - *(ptr_asdata->ptr_depth_adjust) == 2) { /* citstyle-following */
	strcpy(table_name, "CITSTYLE");
	strcpy(column_name, (ptr_asdata->ptr_first)->elname);
	n_chardata = 1;
	n_attrdata = 1;
	current_id = *(ptr_asdata->ptr_citstyle_id);
      }
      else if (*(ptr_asdata->ptr_depth) - *(ptr_asdata->ptr_depth_adjust) == 3) { /* citstyle-biblionumbering-following */
	strcpy(table_name, "CITSTYLE");
	strcpy(column_name, ((ptr_asdata->ptr_first)->ptr_next)->elname);
	strcat(column_name, (ptr_asdata->ptr_first)->elname);
	n_chardata = 1;
	n_attrdata = 1;
	current_id = *(ptr_asdata->ptr_citstyle_id);
      }
      else {
	if (is_descendant_of(ptr_asdata->ptr_first, "PUBTYPE")) {
	  strcpy(table_name, "REFSTYLE");
	  strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "PUBTYPE", concat));
	  strcpy(pubtype, get_ancestor_attr(ptr_asdata->ptr_first, "PUBTYPE", "TYPE"));
	}
	else if (is_descendant_of(ptr_asdata->ptr_first, "INTEXTDEF")) {
	  strcpy(table_name, "REFSTYLE");
	  strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "INTEXTDEF", concat));
	  strcpy(pubtype, "INTEXT");
	}
	else if (is_descendant_of(ptr_asdata->ptr_first, "AUTHORONLY")) {
	  strcpy(table_name, "REFSTYLE");
	  strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "AUTHORONLY", concat));
	  strcpy(pubtype, "AUTHORONLY");
	}
	else { /* yearonly */
	  strcpy(table_name, "REFSTYLE");
	  strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "YEARONLY", concat));
	  strcpy(pubtype, "YEARONLY");
	}
	n_chardata = 1;
	n_attrdata = 1;
	current_id = *(ptr_asdata->ptr_citstyle_id);
      }
    }
    else if (strcmp((ptr_asdata->ptr_first)->elname, "SEPARATOR") == 0) {
      strcpy(table_name, "SEPARATORS");
      strcpy(column_name, "VALUE");
      n_chardata = 1;
      n_attrdata = 0;
      current_id = *(ptr_asdata->ptr_separator_id);
    }

    else if (strcmp((ptr_asdata->ptr_first)->elname, "THREESEPSEACH") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "THREESEPSLAST") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "TWOSEPS") == 0) {
      strcpy(table_name, "REFSTYLE");
      if (is_descendant_of(ptr_asdata->ptr_first, "PUBTYPE")) {
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "PUBTYPE", concat));
	strcpy(pubtype, get_ancestor_attr(ptr_asdata->ptr_first, "PUBTYPE", "TYPE"));
      }
      else if (is_descendant_of(ptr_asdata->ptr_first, "INTEXTDEF")) {
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "INTEXTDEF", concat));
	strcpy(pubtype, "INTEXT");
      }
      else if (is_descendant_of(ptr_asdata->ptr_first, "AUTHORONLY")) {
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "AUTHORONLY", concat));
	strcpy(pubtype, "AUTHORONLY");
      }
      else { /* yearonly */
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "YEARONLY", concat));
	strcpy(pubtype, "YEARONLY");
      }
      n_chardata = 1;
      n_attrdata = 1;
      current_id = *(ptr_asdata->ptr_citstyle_id);
    }
    
    /* only cdata, no attributes */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "FIRSTSEP") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "SECONDSEP") == 0) {
      strcpy(table_name, "REFSTYLE");
      if (is_descendant_of(ptr_asdata->ptr_first, "PUBTYPE")) {
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "PUBTYPE", concat));
	strcpy(pubtype, get_ancestor_attr(ptr_asdata->ptr_first, "PUBTYPE", "TYPE"));
      }
      else if (is_descendant_of(ptr_asdata->ptr_first, "INTEXTDEF")) {
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "INTEXTDEF", concat));
	strcpy(pubtype, "INTEXT");
      }
      else if (is_descendant_of(ptr_asdata->ptr_first, "AUTHORONLY")) {
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "AUTHORONLY", concat));
	strcpy(pubtype, "AUTHORONLY");
      }
      else { /* yearonly */
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "YEARONLY", concat));
	strcpy(pubtype, "YEARONLY");
      }
      n_chardata = 1;
      current_id = *(ptr_asdata->ptr_citstyle_id);
    }

    /* refstyle elements with attributes only */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "AUTHORLIST") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "ISSUE") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "JOURNALNAME") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "NAMEFIRST") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "NAMEOTHER") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "PAGERANGE") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "PAGES") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "PUBDATE") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "PUBLISHER") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "PUBPLACE") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "SERIAL") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "ADDRESS") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "URL") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "NOTES") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "ABSTRACT") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "USERDEF") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "TYPEOFWORK") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "AREA") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "OSTYPE") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "DEGREE") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "RUNNINGTIME") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "CLASSCODEINTL") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "CLASSCODEUS") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "SENDEREMAIL") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "RECIPIENTEMAIL") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "MEDIATYPE") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "NUMVOLUMES") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "EDITION") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "COMPUTER") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "CONFERENCELOCATION") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "REGISTRYNUM") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "CLASSIFICATION") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "SECTION") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "PAMPHLETNUM") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "CHAPTERNUM") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "LINK") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "REFNUMBER") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "CITEKEY") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "SINGLEPAGE") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "TITLE") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "VOLUME") == 0) {
      strcpy(table_name, "REFSTYLE");
      if (is_descendant_of(ptr_asdata->ptr_first, "PUBTYPE")) {
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "PUBTYPE", concat));
	strcpy(pubtype, get_ancestor_attr(ptr_asdata->ptr_first, "PUBTYPE", "TYPE"));
      }
      else if (is_descendant_of(ptr_asdata->ptr_first, "INTEXTDEF")) {
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "INTEXTDEF", concat));
	strcpy(pubtype, "INTEXT");
      }
      else if (is_descendant_of(ptr_asdata->ptr_first, "AUTHORONLY")) {
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "AUTHORONLY", concat));
	strcpy(pubtype, "AUTHORONLY");
      }
      else { /* yearonly */
	strcpy(column_name, concat_elnames(ptr_asdata->ptr_first, "YEARONLY", concat));
	strcpy(pubtype, "YEARONLY");
      }
      n_attrdata = 1;
      current_id = *(ptr_asdata->ptr_citstyle_id);
    }

    /* citstyle elements with attributes only, no column_name req */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "CITSTYLE") == 0
	     || strcmp((ptr_asdata->ptr_first)->elname, "BIBSTYLE") == 0) {
      strcpy(table_name, "CITSTYLE");
      n_attrdata = 1;
      current_id = *(ptr_asdata->ptr_citstyle_id);
    }

    /* citstyle elements with attributes only, column_name req */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "JAN") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "FEB") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "MAR") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "APR") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "MAY") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "JUN") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "JUL") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "AUG") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "SEP") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "OCT") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "NOV") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "DEC") == 0) {
      strcpy(table_name, "CITSTYLE");
      strcpy(column_name, (ptr_asdata->ptr_first)->elname);
      n_attrdata = 1;
      current_id = *(ptr_asdata->ptr_citstyle_id);
    }

    /* this set is done. check whether we replace an existing entry */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "CITESTYLE") == 0) {
      (*(ptr_asdata->ptr_set_count))++;
      sprintf(sql_command, "408:%u:%s\n", *(ptr_asdata->ptr_citstyle_id), ptr_asdata->the_journal);

      if ((new_msg = mstrcat(ptr_asdata->ptr_addresult->msg, sql_command, &(ptr_asdata->ptr_addresult->msg_len), 0)) == NULL) {
	(*(ptr_asdata->ptr_nmem_error))++;
      }
      else {
	ptr_asdata->ptr_addresult->msg = new_msg;
      }
	
      /* the_journal is already quoted */
      sprintf(sql_command, "SELECT ID FROM CITSTYLE WHERE JOURNAL=%s ORDER BY ID ASC", ptr_asdata->the_journal);
      dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (dbires) {
	if (dbi_result_next_row(dbires) != 0) {
	  n_id = my_dbi_result_get_int_idval(dbires, "ID");
	  if (n_id != *(ptr_asdata->ptr_citstyle_id)) {
	    if (!remove_style(ptr_asdata->conn, n_id)) {
	      sprintf(sql_command, "419:%u:%s\n", n_id, ptr_asdata->the_journal);
	    }
	    else {
	      sprintf(sql_command, "420:%u:%s\n", *(ptr_asdata->ptr_citstyle_id), ptr_asdata->the_journal);
	    }
	    
	    if ((new_msg = mstrcat(ptr_asdata->ptr_addresult->msg, sql_command, &(ptr_asdata->ptr_addresult->msg_len), 0)) == NULL) {
	      (*(ptr_asdata->ptr_nmem_error))++;
	    }
	    else {
	      ptr_asdata->ptr_addresult->msg = new_msg;
	    }
	  }
	}
	else {
	  LOG_PRINT(LOG_WARNING, "error in retrieving sql result");
	}
	dbi_result_free(dbires);
      }
      else {
	LOG_PRINT(LOG_WARNING, "error in sql command");
      }
    }

    /* elements without values or attributes */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "AUTHORNAMES") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "AUTHORSEPS") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "TEXT") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "TEXTED") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "TEXTMULTIPLE") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "TEXTSINGLE") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "THREESEPS") == 0 ||
	     strcmp((ptr_asdata->ptr_first)->elname, "REFSTYLE") == 0  ||
	     strcmp((ptr_asdata->ptr_first)->elname, "MONTHS") == 0) {
      /* do nothing (n_chardata and n_attrdata remain 0) */
    }
    /*    printf("%d", depth); */
    /*    for (i = 0; i < depth; i++) { */
    /*      printf("  "); */
    /*    } */
    /*    printf("endtag:%s\n", ptr_first->elname); */

    /* no values or attributes, but we have to take care of depth_adjust */
    else if (strcmp((ptr_asdata->ptr_first)->elname, "STYLESET") == 0) {
      (*(ptr_asdata->ptr_depth_adjust))--;
    }

    if (n_chardata) {
      char* elvalue_q = NULL;

      /* quote element value */
      if ((elvalue_q = strdup((ptr_asdata->ptr_first)->ptr_elvalue)) == NULL) {
	/* todo: bail out */
      }
      else {
	if (dbi_conn_quote_string(ptr_asdata->conn, &elvalue_q) == 0) {
	  /* todo: bail out */
	}
      }

      if (elvalue_q) {
	if (pubtype[0]) {
	  sprintf(sql_command, "UPDATE REFSTYLE SET %s=%s WHERE CITSTYLEID=%u AND PUBTYPE='", column_name, elvalue_q, current_id);
	  /* we have to add pubtype outside of the sprintf call. The reason is unclear, but may be a hidden overflow or something */
	  strcat(sql_command, pubtype);
	  strcat(sql_command, "\'");
	}
	else {
	  sprintf(sql_command, "UPDATE %s SET %s=%s WHERE ID=%u", table_name, column_name, elvalue_q, current_id);
	}

	free(elvalue_q);
	dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
	LOG_PRINT(LOG_DEBUG, sql_command);
	if (!dbires) {
	  (*(ptr_asdata->ptr_ndb_error))++;
	  LOG_PRINT(LOG_WARNING, "error in sql command");
	}
	else {
	  dbi_result_free(dbires);
	}
      } /* end if elvalue_q */
    } /* end if n_chardata */
  } /* end if no mem or db errors */

  /* remove attributes of the current element from the list */
  ptr_attr_remove = (ptr_asdata->ptr_first)->ptr_attr_first;
  while (ptr_attr_remove) {
    /* assemble column name for attribute */
    if (n_attrdata) {
      strcpy(column_name_attribute, column_name);
      strcat(column_name_attribute, ptr_attr_remove->attribute_name);
      /*      for (i = 0; i < depth; i++) { */
      /*        printf("  "); */
      /*      } */
      if (strcmp(ptr_attr_remove->attribute_name, "ROLE") != 0 || !ignore_role) {
	if (pubtype[0]) {
	  sprintf(sql_command, "UPDATE REFSTYLE SET %s='%s' WHERE CITSTYLEID=%u AND PUBTYPE='", column_name_attribute, ptr_attr_remove->attribute_value, current_id);
	  /* we have to add pubtype outside of the sprintf call. The reason is unclear, but may be a hidden overflow or something */
	  strcat(sql_command, pubtype);
	  strcat(sql_command, "\'");
	}
	else {
	  sprintf(sql_command, "UPDATE %s SET %s='%s' WHERE ID=%u", table_name, column_name_attribute, ptr_attr_remove->attribute_value, current_id);
	}

	/*        printf("%s\n", sql_command); */
	dbires = dbi_conn_query(ptr_asdata->conn, sql_command);
	LOG_PRINT(LOG_DEBUG, sql_command);
	if (!dbires) {
	  (*(ptr_asdata->ptr_ndb_error))++;
	  LOG_PRINT(LOG_WARNING, "error in sql command");
	}
	else {
	  dbi_result_free(dbires);
	}
      }
    }
    (ptr_asdata->ptr_first)->ptr_attr_first = ((ptr_asdata->ptr_first)->ptr_attr_first)->ptr_next;
    free(ptr_attr_remove);
    ptr_attr_remove = (ptr_asdata->ptr_first)->ptr_attr_first;
  }

  /* free element value string */
  if ((ptr_asdata->ptr_first)->ptr_elvalue) {
    free((ptr_asdata->ptr_first)->ptr_elvalue);
  }

  /* remove current element from element stack */
  ptr_el_remove = ptr_asdata->ptr_first;
  ptr_asdata->ptr_first = (ptr_asdata->ptr_first)->ptr_next;
  free(ptr_el_remove);

/*    printf("end tag handler done\n"); */
}  /* End of end_handler */

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  char_handler(): handler for character data. It is limited to 256
                  chars per element which is way enough for the
                  citestyle dtd.

  void char_handler has no return value

  void* ptr_data this is a ptr to "non-global" global data that all
             handlers share - will be cast to type struct addstyle_data*

  const char *string ptr to a string containing the char data
                     this string is not \0 terminated and could be
                     only a part of the character data of an element!

  int len length length of string

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void char_handler(void *ptr_data, const char *string, int len) {
  size_t len_elvalue;
  size_t len_string;
  struct addstyle_data* ptr_asdata;

  ptr_asdata = (struct addstyle_data*)ptr_data;
  
  if (*(ptr_asdata->ptr_ndb_error) || *(ptr_asdata->ptr_nmem_error)) {
    return;
  }
  len_elvalue = strlen((ptr_asdata->ptr_first)->ptr_elvalue);
  len_elvalue = (len_elvalue > 256) ? 256 : len_elvalue;
  len_string = (len > (int)(256-(int)len_elvalue)) ? 256-len_elvalue : (size_t)len;
  strncpy(&(((ptr_asdata->ptr_first)->ptr_elvalue)[len_elvalue]), string, len_string);
  ((ptr_asdata->ptr_first)->ptr_elvalue)[len_elvalue + len_string] = '\0';
/*    printf(">>%s\n", ptr_first->elvalue); */
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  get_ancestor_attr(): returns the value of an attribute of an
                       ancestor of the current element

  static char* get_ancestor_attr returns the value of the requested attribute
                       or NULL if no such attribute is found

  struct elstack* ptr_current_element ptr to the current element on
                       the element stack

  char* ancestor_name name of the requested ancestor element

  char* ancestor_attribute name of the requested ancestor attribute

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* get_ancestor_attr(struct elstack* ptr_current_element, char* ancestor_name, char* ancestor_attribute) {
  struct elstack* ptr_el;
  struct attrlist* ptr_attr;

  ptr_el = ptr_current_element;

  while (ptr_el != NULL) {
    if (strcmp(ptr_el->elname, ancestor_name) == 0) {
      ptr_attr = ptr_el->ptr_attr_first;

      while (ptr_attr != NULL) {
	if (strcmp(ptr_attr->attribute_name, ancestor_attribute) == 0) {
	  return ptr_attr->attribute_value;
	}
	ptr_attr = ptr_attr->ptr_next;
      }
    }
    ptr_el = ptr_el->ptr_next;
  }
  return NULL;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  get_attr(): returns the value of an attribute of the current element

  char* get_attr returns the value of the requested attribute
                       or NULL if no such attribute is found

  struct elstack* ptr_current_element ptr to the current element on
                       the element stack

  char* attribute name of the requested attribute

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* get_attr(struct elstack* ptr_current_element, char* attribute) {
  struct attrlist* ptr_attr;

  ptr_attr = ptr_current_element->ptr_attr_first;

  while (ptr_attr != NULL) {
    if (strcmp(ptr_attr->attribute_name, attribute) == 0) {
      return ptr_attr->attribute_value;
    }
    ptr_attr = ptr_attr->ptr_next;
  }

  return NULL;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  is_descendant_of(): tests whether the current element is the
                       descendant of a given element

  int is_descendant_of returns 1 if the current element is the
                       descendant of the given element, 0 if not

  struct elstack* ptr_current_element ptr to the current element on
                       the element stack

  char* ancestor_name name of the requested ancestor element

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int is_descendant_of(struct elstack* ptr_current_element, char* ancestor_name) {
  struct elstack* ptr_el;

  ptr_el = ptr_current_element;
  
  while (ptr_el != NULL) {
    if (strcmp(ptr_el->elname, ancestor_name) == 0) {
      return 1;
    }
    ptr_el = ptr_el->ptr_next;
  }
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  concat_elnames(): concatenates the names of all ancestors of the
                    current element up to a given element which is not
                    to be concatenated to the string. The leftmost
                    element name in the concatenated string will be
                    of the distal element, the rightmost will be the 
                    name of the current element

  static char* concat_elnames returns a ptr to the concatenated names.
                    This will be an empty string if the current element
		    is identical with the first excluded element

  struct elstack* ptr_current_element ptr to the current element on the
                    element stack

  char* exclude_name the name of the first ancestor (as seen from the
                    current element) that is not to be included into
                    the concatenated names. This is where the concatenating
                    stops.

  char* concat ptr to a buffer that receives the concatenated name. This
                    buffer must hold at least 64 chars. This ptr is also
                    used as the return value for the function

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* concat_elnames(struct elstack* ptr_current_element, char* exclude_name, char* concat) {
  struct elstack* ptr_el;
  char buffer[64];
  char* ptr_attr;

  concat[0] = '\0';
  ptr_el = ptr_current_element;
 
  while (ptr_el != NULL) {
    if (strcmp(ptr_el->elname, exclude_name) == 0) {
      return concat;
    }
    strcpy(buffer, concat);
    /* AUTHORLIST, PUBDATE, TITLE, USERDEF, MISC, LINK need special care
       as the actual column name in the database depends on the ROLE
       attribute */
    if (strcmp(ptr_el->elname, "AUTHORLIST") == 0) {
      /* we use short versions of these element names in order not
	 to exceed the column name limit of PostgreSQL */
      ptr_attr = get_ancestor_attr(ptr_el, "AUTHORLIST", "ROLE");
      if (ptr_attr && strcmp(ptr_attr, "PART") == 0) {
	strcpy(concat, "Q"); /* AUTHORLIST */
      }
      else if (ptr_attr && strcmp(ptr_attr, "SERIES") == 0) {
	strcpy(concat, "Y"); /* SEDITORLIST */
      }
      else if (ptr_attr && strcmp(ptr_attr, "ALL") == 0) {
	strcpy(concat, "Z"); /* ALLALIST */
      }
      else {
	/* DTD uses this as default */
	strcpy(concat, "X"); /* EDITORLIST */
      }
    }
    else if (strcmp(ptr_el->elname, "PUBDATE") == 0) {
      ptr_attr = get_ancestor_attr(ptr_el, "PUBDATE", "ROLE");
      if (ptr_attr && strcmp(ptr_attr, "SECONDARY") == 0) {
	strcpy(concat, "PUBDATESEC");
      }
      else if (ptr_attr && strcmp(ptr_attr, "ALL") == 0) {
	strcpy(concat, "PUBDATEALL");
      }
      else {
	strcpy(concat, "PUBDATE");
      }
    }
    else if (strcmp(ptr_el->elname, "TITLE") == 0) {
      ptr_attr = get_ancestor_attr(ptr_el, "TITLE", "ROLE");
      if (ptr_attr && strcmp(ptr_attr, "PART") == 0) {
	strcpy(concat, "TITLE");
      }
      else if (ptr_attr && strcmp(ptr_attr, "SERIES") == 0) {
	strcpy(concat, "SERIESTITLE");
      }
      else if (ptr_attr && strcmp(ptr_attr, "ALL") == 0) {
	strcpy(concat, "ALLTITLE");
      }
      else {
	/* DTD uses this as default */
	strcpy(concat, "BOOKTITLE");
      }
    }
    else if (strcmp(ptr_el->elname, "USERDEF") == 0) {
      ptr_attr = get_ancestor_attr(ptr_el, "USERDEF", "ROLE");
      if (ptr_attr && strcmp(ptr_attr, "2") == 0) {
	strcpy(concat, "USERDEF2");
      }
      else if (ptr_attr && strcmp(ptr_attr, "3") == 0) {
	strcpy(concat, "USERDEF3");
      }
      else if (ptr_attr && strcmp(ptr_attr, "4") == 0) {
	strcpy(concat, "USERDEF4");
      }
      else if (ptr_attr && strcmp(ptr_attr, "5") == 0) {
	strcpy(concat, "USERDEF5");
      }
      else {
	strcpy(concat, "USERDEF1");
      }
    }
    else if (strcmp(ptr_el->elname, "LINK") == 0) {
      ptr_attr = get_ancestor_attr(ptr_el, "LINK", "ROLE");
      if (ptr_attr && strcmp(ptr_attr, "1") == 0) {
	strcpy(concat, "LINK1");
      }
      else if (ptr_attr && strcmp(ptr_attr, "2") == 0) {
	strcpy(concat, "LINK2");
      }
      else if (ptr_attr && strcmp(ptr_attr, "3") == 0) {
	strcpy(concat, "LINK3");
      }
      else if (ptr_attr && strcmp(ptr_attr, "4") == 0) {
	strcpy(concat, "LINK4");
      }
      else {
	strcpy(concat, "LINK0");
      }
    }
    /* in all other cases we can simply use the element name */
    else {
      strcpy(concat, ptr_el->elname);
    }
    strcat(concat, buffer);
    ptr_el = ptr_el->ptr_next;
  }
  return concat;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  delete_list(): deletes the linked element list and the associated
                 attribute lists. This is to free the memory in case
                 of an error

  void delete_list

  struct elstack* ptr_current_element ptr to the first entry in the
                  linked list

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void delete_list(struct elstack* ptr_current_element) {
  struct elstack* ptr_el;
  struct attrlist* ptr_attr;
  struct elstack* ptr_el_next;
  struct attrlist* ptr_attr_next;

  ptr_el = ptr_current_element;

  while (ptr_el != NULL) {
    ptr_attr = ptr_el->ptr_attr_first;

    while (ptr_attr != NULL) {
      ptr_attr_next = ptr_attr->ptr_next;
      free(ptr_attr);
      ptr_attr = ptr_attr_next;
    }
    ptr_el_next = ptr_el->ptr_next;
    free(ptr_el);
    ptr_el = ptr_el_next;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  id_start_handler(): handler for start tags (handling citation/xref
                      data)
  
  void start_handler has no return value

  void* ptr_data this is a ptr to "non-global" global data that all
             handlers share - will be cast to type struct getbib_data*

  const char *el ptr to a string containing the element name

  const char** ptr_attr ptr to an array of attributes (name-value pairs)

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void id_start_handler(void *ptr_data, const char *el, const char **ptr_attr) {
  struct simple_elstack* ptr_el_new;
  struct getbib_data* ptr_gbdata;

  ptr_gbdata = (struct getbib_data*)ptr_data;

  /* add current element to element stack */
  ptr_el_new = malloc(sizeof(struct simple_elstack));
  if (ptr_el_new == NULL) {
    (*(ptr_gbdata->ptr_nmem_error))++;
  }
  else {
    /*    printf("have memory\n"); */
    strncpy(ptr_el_new->elname, el, 63);
    ptr_el_new->elname[63] = '\0'; /* terminate just in case */
    ptr_el_new->elvalue = malloc(256); /* something to start with */
    if (ptr_el_new->elvalue == NULL) {
      (*(ptr_gbdata->ptr_nmem_error))++;
    }
    else {
      ptr_el_new->elvalue[0] = '\0';
      ptr_el_new->ptr_snext = ptr_gbdata->ptr_sfirst;
      ptr_gbdata->ptr_sfirst = ptr_el_new;
    }
    /*    printf("%s", ptr_first->elname); */

    /* add attribute */
    if (ptr_attr[0] && ptr_attr[1]) {
      ptr_el_new->attrvalue = mstrdup((char*)ptr_attr[1]);
      if (!ptr_el_new->attrvalue) {
	(*(ptr_gbdata->ptr_nmem_error))++;
      }
    }
    else {
      ptr_el_new->attrvalue = NULL;
    }
  }
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  id_end_handler(): handler for end tags (handling citation/xref data)

  void end_handler has no return value

  void* ptr_data this is a ptr to "non-global" global data that all
             handlers share - will be cast to type struct getbib_data*

  const char *el ptr to a string containing the element name

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void id_end_handler(void *ptr_data, const char *el) {
  size_t sql_command_len;
  int nis_multi = 0;
  int errcode; /* receives error code for periodical requests */
  char buffer[32];
  char* database = "";
  char* id_string;
  char* multi_id_string;
  char* multi_database = "";
  char* sql_command;
  char* new_sql_command;
  const char* query_result;
  char* new_el;
  char* new_elvalue;
  char* new_id;
  char* new_attrvalue = NULL;
  char* item;
  char* reftype = NULL;
  char* my_aempty = NULL;
  char *my_refdb_title = NULL;
  char* escape_buffer;
  char titlespec[16];
  char periodical[256] = "";
  const char *drivername; /* name of the libdbi driver */
  int use_title_as_author = 0;
  unsigned long long temp_id = 0;
  unsigned long long real_orig_id = 0;
  dbi_conn conn_source = NULL;
  dbi_result dbires;
  dbi_result dbires1;
  struct getbib_data* ptr_gbdata;
  struct simple_elstack* ptr_el_remove;

  ptr_gbdata = (struct getbib_data*)ptr_data;

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

  if ((new_el = mstrdup((char*)el)) == NULL) {
    (*(ptr_gbdata->ptr_nmem_error))++;
    return;
  }

  strup(new_el);

  if ((new_elvalue = mstrdup((char*)((ptr_gbdata->ptr_sfirst)->elvalue))) == NULL) {
    (*(ptr_gbdata->ptr_nmem_error))++;
    return;
  }

  if ((ptr_gbdata->ptr_sfirst)->attrvalue && (new_attrvalue = mstrdup((char*)((ptr_gbdata->ptr_sfirst)->attrvalue))) == NULL) {
    (*(ptr_gbdata->ptr_nmem_error))++;
    return;
  }


  if (strcmp(new_el, "XREF") == 0) {
    sql_command_len = 512;
    if ((sql_command = malloc(sql_command_len)) == NULL) {
      (*(ptr_gbdata->ptr_nmem_error))++;
      return;
    }
/*     strup((ptr_gbdata->ptr_sfirst)->elvalue); */
    id_string = getid((ptr_gbdata->ptr_sfirst)->elvalue, &database, &nis_multi);

    nis_multi = 0; /* protects against malformed linkends */
    if ((ptr_gbdata->ptr_sfirst)->attrvalue) {
      multi_id_string = getid((ptr_gbdata->ptr_sfirst)->attrvalue, &multi_database, &nis_multi);
    }

    if (database) {
      strdn(database); /* use lowercase name */
    }
    else {
      database = ptr_gbdata->ptr_default_db; /* use default database */
    }


    /* check whether orig_id is numerical */
    if (!is_number(id_string)) {
      /* retrieve corresponding ID for this citekey */
      if (!strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn, "multiple_db"), "t")) {
	sprintf(sql_command, "SELECT refdb_id, refdb_type FROM %s.t_refdb WHERE refdb_citekey='%s' AND refdb_type!='DUMMY'", database, id_string);
	dbires = dbi_conn_query(ptr_gbdata->conn, sql_command);
      }
      else {
	/* get additional connection */
	if ((conn_source = connect_to_db(ptr_gbdata->ptr_clrequest, database, 0)) == NULL) {
	  LOG_PRINT(LOG_ERR, get_status_msg(205));
	  return;
	}
	sprintf(sql_command, "SELECT refdb_id, refdb_type FROM t_refdb WHERE refdb_citekey='%s' AND refdb_type!='DUMMY'", id_string);
	dbires = dbi_conn_query(conn_source, sql_command);
      }
      
      LOG_PRINT(LOG_DEBUG, sql_command);
      if (!dbires) {
	LOG_PRINT(LOG_WARNING, "select failed");
	(*(ptr_gbdata->ptr_ndb_error))++;
	real_orig_id = 0;
      }
      else {
	if (dbi_result_next_row(dbires)) {
	  real_orig_id = my_dbi_result_get_idval(dbires, "refdb_id");
	  reftype = my_dbi_result_get_string_copy(dbires, "refdb_type");
	}
	else {
	  real_orig_id = 0;
	}
	dbi_result_free(dbires);
      }
      if (strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn, "multiple_db"), "t") && conn_source) {
	dbi_conn_close(conn_source);
      }
    }
    else { /* if id is numeric */
      /* we still have to query the database as the dataset may be missing */
      if (!strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn, "multiple_db"), "t")) {
	sprintf(sql_command, "SELECT refdb_id, refdb_type FROM %s.t_refdb WHERE refdb_id='%s' AND refdb_type!='DUMMY'", database, id_string);
	dbires = dbi_conn_query(ptr_gbdata->conn, sql_command);
      }
      else {
	/* get additional connection */
	if ((conn_source = connect_to_db(ptr_gbdata->ptr_clrequest, database, 0)) == NULL) {
	  LOG_PRINT(LOG_ERR, get_status_msg(205));
	  return;
	}
	sprintf(sql_command, "SELECT refdb_id, refdb_type FROM t_refdb WHERE refdb_id='%s' AND refdb_type!='DUMMY'", id_string);
	dbires = dbi_conn_query(conn_source, sql_command);
      }
      
      LOG_PRINT(LOG_DEBUG, sql_command);
      
      if (!dbires) {
	LOG_PRINT(LOG_WARNING, "select failed");
	(*(ptr_gbdata->ptr_ndb_error))++;
	real_orig_id = 0;
      }
      else {
	if (dbi_result_next_row(dbires)) {
	  real_orig_id = my_dbi_result_get_idval(dbires, "refdb_id");
	  reftype = my_dbi_result_get_string_copy(dbires, "refdb_type");
	}
	else {
	  real_orig_id = 0;
	}
	dbi_result_free(dbires);
      }

      if (strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn, "multiple_db"), "t") && conn_source) {
	dbi_conn_close(conn_source);
      }
    } /* end if numerical */

  
    if (real_orig_id) {
      if (nis_multi) { /* true only if a correct endterm was encountered */
	if ((new_id = mstrcpy(ptr_gbdata->ptr_curr_multi_id, new_attrvalue, ptr_gbdata->ptr_cmid_len)) == NULL) {
	  (*(ptr_gbdata->ptr_nmem_error))++;
	  return;
	}
	else {
	  ptr_gbdata->ptr_curr_multi_id = new_id;
	}
	free(reftype);
      }
      else {
	if (id_string != NULL) {
	  dbi_driver driver;
	  
	  /* see whether the style requests to use titles in place of
	     missing authors. The style should have the ALTERNATETEXT
	     attribute set to TITLEPART, TITLEPUB, TITLESERIES,
	     TITLEALL (the latter meaning the first available), or to
	     JOURNALNAME */
	  if (!strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn_refdb, "multiple_db"), "t")) {
	    sprintf(sql_command, "SELECT QAEMPTY,XAEMPTY,QALTERNATETEXT,XALTERNATETEXT FROM %s.REFSTYLE INNER JOIN %s.CITSTYLE ON REFSTYLE.CITSTYLEID=CITSTYLE.ID WHERE REFSTYLE.PUBTYPE='%s' AND JOURNAL=%s", main_db, main_db, reftype, ptr_gbdata->quoted_journal);
	  }
	  else {
	    sprintf(sql_command, "SELECT QAEMPTY,XAEMPTY,QALTERNATETEXT,XALTERNATETEXT FROM REFSTYLE INNER JOIN CITSTYLE ON REFSTYLE.CITSTYLEID=CITSTYLE.ID WHERE REFSTYLE.PUBTYPE='%s' AND JOURNAL=%s", reftype, ptr_gbdata->quoted_journal);
	  }

	  LOG_PRINT(LOG_DEBUG, sql_command);
	  dbires = dbi_conn_query(ptr_gbdata->conn_refdb, sql_command);
	  if (!dbires) {
	    LOG_PRINT(LOG_WARNING, "accessing style data failed");
	    (*(ptr_gbdata->ptr_ndb_error))++;
	  }
	  else {
	    if ((use_title_as_author = set_alternatetext(dbires, &my_aempty)) == -1) {
	      /* try GEN instead */
	      dbi_result_free(dbires);
	      if (!strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn_refdb, "multiple_db"), "t")) {
		sprintf(sql_command, "SELECT QAEMPTY,XAEMPTY,QALTERNATETEXT,XALTERNATETEXT FROM %s.REFSTYLE INNER JOIN %s.CITSTYLE ON REFSTYLE.CITSTYLEID=CITSTYLE.ID WHERE REFSTYLE.PUBTYPE='GEN' AND JOURNAL=%s", main_db, main_db, ptr_gbdata->quoted_journal);
	      }
	      else {
		sprintf(sql_command, "SELECT QAEMPTY,XAEMPTY,QALTERNATETEXT,XALTERNATETEXT FROM REFSTYLE INNER JOIN CITSTYLE ON REFSTYLE.CITSTYLEID=CITSTYLE.ID WHERE REFSTYLE.PUBTYPE='GEN' AND JOURNAL=%s", ptr_gbdata->quoted_journal);
	      }

	      LOG_PRINT(LOG_DEBUG, sql_command);
	      dbires = dbi_conn_query(ptr_gbdata->conn_refdb, sql_command);
	      if (!dbires) {
		LOG_PRINT(LOG_WARNING, "accessing style data failed");
		(*(ptr_gbdata->ptr_ndb_error))++;
	      }
	      else {
		if ((use_title_as_author = set_alternatetext(dbires, &my_aempty)) == -1) {
		  (*(ptr_gbdata->ptr_nmem_error))++;
		}
	      }
	    }
	    dbi_result_free(dbires);
	  } /* end if have result */




	  if (!has_part_data(reftype)) {
	    strcpy(titlespec, "refdb_booktitle");
	  }
	  else {
	    strcpy(titlespec, "refdb_title");
	  }
	  free(reftype);

	  driver = dbi_conn_get_driver(ptr_gbdata->conn);

	  /* select items and stash them into the temporary table */
	  /* all fields that we can get directly from t_refdb */
	  /* create a new row with INSERT */
	  if (!strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn, "multiple_db"), "t")) {
	    sprintf(sql_command, "INSERT INTO %s (article_title,volume,issue,startpage,pubyear, citekey) SELECT %s,refdb_volume,refdb_issue,refdb_startpage,refdb_pubyear, refdb_citekey FROM %s.t_refdb WHERE refdb_id="ULLSPEC, ptr_gbdata->ptr_table_name, titlespec, database, (unsigned long long)real_orig_id);
	    LOG_PRINT(LOG_DEBUG, sql_command);
	    dbires = dbi_conn_query(ptr_gbdata->conn, sql_command);
	    if (!dbires) {
	      LOG_PRINT(LOG_WARNING, "temp table insert failed");
	      (*(ptr_gbdata->ptr_ndb_error))++;
	    }
	    else {
	      dbi_result_free(dbires);
	    }
	    
	    /* save new temp id */
	    temp_id = dbi_conn_sequence_last(ptr_gbdata->conn, NULL);

	    /* get refdb_title separately, as we need it further down */
	    sprintf(sql_command, "SELECT %s,refdb_volume,refdb_issue,refdb_startpage,refdb_pubyear, refdb_citekey FROM t_refdb WHERE refdb_id="ULLSPEC, titlespec, (unsigned long long)real_orig_id);
	    LOG_PRINT(LOG_DEBUG, sql_command);
	    dbires = dbi_conn_query(ptr_gbdata->conn, sql_command);
	    if (!dbires) {
	      LOG_PRINT(LOG_WARNING, "source table select failed");
	      (*(ptr_gbdata->ptr_ndb_error))++;
	    }
	    else {
	      if (dbi_result_next_row(dbires)) {
		my_refdb_title = my_dbi_result_get_string_copy(dbires, titlespec);
		/* the quote_string function checks for non-NULL arguments */
		dbi_conn_quote_string(ptr_gbdata->conn, &my_refdb_title);
	      }
	    }
	  }
	  else {
	    /* we've got a small problem here: we can't select values from
	       one database and insert it into another with a single command
	       Instead we've got to do it the hard way */
	    
	    /* get a connection to the source database */
	    if ((conn_source = connect_to_db(ptr_gbdata->ptr_clrequest, database, 0)) == NULL) {
	      LOG_PRINT(LOG_ERR, get_status_msg(205));
	      return;
	    }
	    sprintf(sql_command, "SELECT %s,refdb_volume,refdb_issue,refdb_startpage,refdb_pubyear, refdb_citekey FROM t_refdb WHERE refdb_id="ULLSPEC, titlespec, (unsigned long long)real_orig_id);
	    LOG_PRINT(LOG_DEBUG, sql_command);
	    dbires = dbi_conn_query(conn_source, sql_command);
	    if (!dbires) {
	      LOG_PRINT(LOG_WARNING, "source table select failed");
	      (*(ptr_gbdata->ptr_ndb_error))++;
	    }
	    else {
	      if (dbi_result_next_row(dbires)) {
		/* get strings quoted properly */
		char *my_refdb_volume;
		char *my_refdb_issue;
		char *my_refdb_startpage;
		char *my_refdb_citekey;
		
		my_refdb_title = my_dbi_result_get_string_copy(dbires, titlespec);
		my_refdb_volume = my_dbi_result_get_string_copy(dbires, "refdb_volume");
		my_refdb_issue = my_dbi_result_get_string_copy(dbires, "refdb_issue");
		my_refdb_startpage = my_dbi_result_get_string_copy(dbires, "refdb_startpage");
		my_refdb_citekey = my_dbi_result_get_string_copy(dbires, "refdb_citekey");
		
		/* the quote_string function checks for non-NULL arguments */
		dbi_conn_quote_string(ptr_gbdata->conn, &my_refdb_title);
		dbi_conn_quote_string(ptr_gbdata->conn, &my_refdb_volume);
		dbi_conn_quote_string(ptr_gbdata->conn, &my_refdb_issue);
		dbi_conn_quote_string(ptr_gbdata->conn, &my_refdb_startpage);
		dbi_conn_quote_string(ptr_gbdata->conn, &my_refdb_citekey);
		
		sprintf(sql_command, "INSERT INTO %s (article_title,volume,issue,startpage,pubyear, citekey) VALUES (%s,%s,%s,%s,%d, %s)", ptr_gbdata->ptr_table_name, (my_refdb_title) ? my_refdb_title : "\'\'", (my_refdb_volume) ? my_refdb_volume : "\'\'", (my_refdb_issue) ? my_refdb_issue : "\'\'", (my_refdb_startpage) ? my_refdb_startpage : "\'\'", dbi_result_get_short(dbires, "refdb_pubyear"), (my_refdb_citekey) ? my_refdb_citekey : "\'\'");
		LOG_PRINT(LOG_DEBUG, sql_command);
		
		if (my_refdb_volume) {
		  free(my_refdb_volume);
		}
		if (my_refdb_issue) {
		  free(my_refdb_issue);
		}
		if (my_refdb_startpage) {
		  free(my_refdb_startpage);
		}
		
		dbires1 = dbi_conn_query(ptr_gbdata->conn, sql_command);
		if (!dbires1) {
		  LOG_PRINT(LOG_WARNING, "temp table insert failed");
		  (*(ptr_gbdata->ptr_ndb_error))++;
		}
		else {
		  dbi_result_free(dbires1);
		}
		/* save new temp id */
		temp_id = dbi_conn_sequence_last(ptr_gbdata->conn, "getbibtemp_id_seq");
	      }
	      dbi_result_free(dbires);
	    }
	  }
	  
	  /* dbname, orig_id, entry_id  */
	  sprintf(sql_command, "UPDATE %s SET dbname='%s',citation_pos=%d,xref_pos=%d,orig_id="ULLSPEC",entry_id='%s', multi_id='%s' WHERE id="ULLSPEC, ptr_gbdata->ptr_table_name, database, *(ptr_gbdata->ptr_citation_count), *(ptr_gbdata->ptr_xref_count), (unsigned long long)real_orig_id, new_elvalue, ptr_gbdata->ptr_curr_multi_id, (unsigned long long)temp_id);
	  LOG_PRINT(LOG_DEBUG, sql_command);
	  dbires = dbi_conn_query(ptr_gbdata->conn, sql_command);
	  if (!dbires) {
	    LOG_PRINT(LOG_WARNING, "temp table insert failed");
	    (*(ptr_gbdata->ptr_ndb_error))++;
	  }
	  else {
	    dbi_result_free(dbires);
	    
	    /* author_concat */
	    sprintf(sql_command, "UPDATE %s SET author_concat=\'", ptr_gbdata->ptr_table_name);
	    
	    if (!strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn, "multiple_db"), "t")) {
	      dbires1 = request_authors(ptr_gbdata->conn, 4 /* first available */, NULL /* all roles */, database, 0, real_orig_id); 
	    }
	    else {
	      dbires1 = request_authors(conn_source, 4 /* first available */, NULL /* all roles */, NULL, 0, real_orig_id); 
	    }
	    if (!dbires1) {
	      LOG_PRINT(LOG_WARNING, "temp table author query failed");
	      (*(ptr_gbdata->ptr_ndb_error))++;
	    }
	    else {
	      if (!get_num_authors(dbires1)) {
/*  		printf("num_authors went to zero\n");  */
		/* if no authors, use AEMPTY or title here */
		if (my_aempty) {
/*  		  printf("use xaempty %s\n", my_aempty);  */
		  /* my_aempty is quoted, but we need only the
		     internal quotes */
		  my_aempty[strlen(my_aempty)-1] = '\0';
		  if ((new_sql_command = mstrcat(sql_command, my_aempty + 1, &sql_command_len, 0)) == NULL) {
		    (*(ptr_gbdata->ptr_nmem_error))++;
		  }
		  else {
		    sql_command = new_sql_command;
		  }
		}
		/* use journalname as author */
		else if (use_title_as_author == 5) {
		  if (!strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn, "multiple_db"), "t")) {
		    item = get_periodical(ptr_gbdata->conn, periodical, database, 4, &errcode, 0, real_orig_id, NULL /* no frequency required */); 
		  }
		  else {
		    item = get_periodical(conn_source, periodical, NULL, 4, &errcode, 0, real_orig_id, NULL /* no frequency required */); 
		  }
	  
		  if (!item && errcode != 1) {
		    LOG_PRINT(LOG_WARNING, "temp table periodical query failed");
		    (*(ptr_gbdata->ptr_ndb_error))++;
		  }
		  else if (!errcode) {
		    if (*periodical) {
		      /* 	      printf("periodical went to:%s<<\n", periodical); */
		      /* string is short enough, don't need mstrcat here */
		      escape_buffer = strdup(item);

		      if (!escape_buffer) {
			LOG_PRINT(LOG_WARNING, "malloc failed");
			(*(ptr_gbdata->ptr_nmem_error))++;
		      }

		      /* escape any characters that the database server cannot digest */
		      if (dbi_conn_quote_string(ptr_gbdata->conn, &escape_buffer) == 0) {
			LOG_PRINT(LOG_WARNING, "out of memory");
			(*(ptr_gbdata->ptr_nmem_error))++;
		      }

		      /* 	      printf("escape_buffer went to:%s<<\n", escape_buffer); */
		      escape_buffer[strlen(escape_buffer)-1] = '\0';
		      if ((new_sql_command = mstrcat(sql_command, escape_buffer + 1, &sql_command_len, 0)) == NULL) {
			(*(ptr_gbdata->ptr_nmem_error))++;
		      }
		      else {
			sql_command = new_sql_command;
		      }

		      free(escape_buffer);
		    }
		  }
		}
		/* use title as author */
		else if (use_title_as_author) {
/*  		  printf("use qtitle %s\n", my_refdb_title);  */
		  if (my_refdb_title) {
		    /* my_refdb_title is quoted, but we need only the
		     internal quotes */
		    my_refdb_title[strlen(my_refdb_title)-1] = '\0';
		    if ((new_sql_command = mstrcat(sql_command, my_refdb_title + 1, &sql_command_len, 0)) == NULL) {
		      (*(ptr_gbdata->ptr_nmem_error))++;
		    }
		    else {
		      sql_command = new_sql_command;
		    }
		  }
		}
		else {
/* 		  printf("do nothing\n"); */
		}
	      }
	      else { /* have authors */
/* 		printf("have at least one author\n"); */
		/* reset */
		use_title_as_author = 0;
		while ((query_result = get_author(dbires1)) != NULL) {
		  char *author_q;

		  author_q = strdup(query_result);
		  if (!author_q || dbi_conn_quote_string(ptr_gbdata->conn, &author_q) == 0) {
		    LOG_PRINT(LOG_WARNING, "out of memory");
		    (*(ptr_gbdata->ptr_nmem_error))++;
		  }
		  else if (*author_q) {
		    /* the quoted author is enclosed in quotes (who
		       would have thought?) but we don't need these
		       here; we need only the internal quotes */
		    author_q[strlen(author_q)-1] = '\0';
		    
		    if ((new_sql_command = mstrcat(sql_command, author_q + 1, &sql_command_len, 0)) == NULL) {
		      (*(ptr_gbdata->ptr_nmem_error))++;
		      break;
		    }
		    else {
		      sql_command = new_sql_command;
		    }

		    free(author_q);
		  } /* end if no mem error */
		} /* end while have author */
	      } /* end if have authors */
	      
	      if (use_title_as_author && my_refdb_title) {
		char buffer[64];

		sprintf(buffer, "\', title_as_author=%d WHERE id=", use_title_as_author);
		if ((new_sql_command = mstrcat(sql_command, buffer, &sql_command_len, 0)) == NULL) {
		  (*(ptr_gbdata->ptr_nmem_error))++;
		}
		else {
		  sql_command = new_sql_command;
		}
	      }
	      else {
		/* title_as_author has a default of 0, no need to alter it */
		if ((new_sql_command = mstrcat(sql_command, "\' WHERE id=", &sql_command_len, 0)) == NULL) {
		  (*(ptr_gbdata->ptr_nmem_error))++;
		}
		else {
		  sql_command = new_sql_command;
		}
	      }
	      clean_request(dbires1);
	      
	      sprintf(buffer, ULLSPEC, (unsigned long long)temp_id);
	      if ((new_sql_command = mstrcat(sql_command, buffer, &sql_command_len, 0)) == NULL) {
		(*(ptr_gbdata->ptr_nmem_error))++;
	      }
	      else {
		sql_command = new_sql_command;
		LOG_PRINT(LOG_DEBUG, sql_command);
		dbires1 = dbi_conn_query(ptr_gbdata->conn, sql_command);
		if (!dbires1) {
		  LOG_PRINT(LOG_WARNING, "temp table insert failed");
		  (*(ptr_gbdata->ptr_ndb_error))++;
		}
		dbi_result_free(dbires1);
	      }
	    }
	  }

	  if (my_refdb_title) {
	    free(my_refdb_title);
	  }

	  /* journal */
	  sprintf(sql_command, "UPDATE %s SET periodical=", ptr_gbdata->ptr_table_name);
	  
	  if (!strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn, "multiple_db"), "t")) {
	    item = get_periodical(ptr_gbdata->conn, periodical, database, 4, &errcode, 0, real_orig_id, NULL /* no frequency required */); 
	  }
	  else {
	    item = get_periodical(conn_source, periodical, NULL, 4, &errcode, 0, real_orig_id, NULL /* no frequency required */); 
	  }
	  
	  if (!item && errcode != 1) {
	    LOG_PRINT(LOG_WARNING, "temp table periodical query failed");
	    (*(ptr_gbdata->ptr_ndb_error))++;
	  }
	  else if (!errcode) {
	    if (*periodical) {
/* 	      printf("periodical went to:%s<<\n", periodical); */
	      /* string is short enough, don't need mstrcat here */
	      escape_buffer = strdup(item);

	      if (!escape_buffer) {
		LOG_PRINT(LOG_WARNING, "malloc failed");
		(*(ptr_gbdata->ptr_nmem_error))++;
	      }

	      /* escape any characters that the database server cannot digest */
	      if (dbi_conn_quote_string(ptr_gbdata->conn, &escape_buffer) == 0) {
		LOG_PRINT(LOG_WARNING, "out of memory");
		(*(ptr_gbdata->ptr_nmem_error))++;
	      }

/* 	      printf("escape_buffer went to:%s<<\n", escape_buffer); */
	      strcat(sql_command, escape_buffer);
	      free(escape_buffer);
	    }
	    strcat(sql_command, " WHERE id=");
	    /* buffer still has temp_id*/
	    strcat(sql_command, buffer);
	    LOG_PRINT(LOG_DEBUG, sql_command);
	    dbires = dbi_conn_query(ptr_gbdata->conn, sql_command);
	    if (!dbires) {
	      LOG_PRINT(LOG_WARNING, "temp table insert failed");
	      (*(ptr_gbdata->ptr_ndb_error))++;
	    }
	    dbi_result_free(dbires);
	  }
	  
	  /* close extra connection */
	  if (strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn, "multiple_db"), "t") && conn_source) {
	    dbi_conn_close(conn_source);
	  }
	}
	(*(ptr_gbdata->ptr_xref_count))++;
      }
    }
    else { /* ID not found */
      /* don't count the first dummy citation in a multiple citation */
      if (!nis_multi) {
	/* requested ref is not in the database */
	(*(ptr_gbdata->ptr_ndb_notfound))++;
 
	/* add ID or citation key to the linked list of unfound stuff */
	if (append_lilifstring(ptr_gbdata->ptr_notfound_first, id_string)) {
	  (*(ptr_gbdata->ptr_ndb_error))++;
	}
      }
    }

    free(sql_command);
  }
  else if (strcmp(new_el, "CITATION") == 0) {
    (*(ptr_gbdata->ptr_citation_count))++;
    (*(ptr_gbdata->ptr_xref_count)) = 0;

    /* reset multi-id */
    (ptr_gbdata->ptr_curr_multi_id)[0] = '\0';
  } /* end if XREF or CITATION */

  /* remove current element from element stack */
  ptr_el_remove = ptr_gbdata->ptr_sfirst;
  ptr_gbdata->ptr_sfirst = (ptr_gbdata->ptr_sfirst)->ptr_snext;
  free(ptr_el_remove->elvalue);
  if (ptr_el_remove->attrvalue) {
    free(ptr_el_remove->attrvalue);
  }
  free(ptr_el_remove);
  
  free(new_el);
  free(new_elvalue);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  id_char_handler(): handler for character data (getref). 

  void char_handler has no return value

  void* ptr_data this is a ptr to "non-global" global data that all
             handlers share - will be cast to type struct getbib_data*

  const char *string ptr to a string containing the char data
                     this string is not \0 terminated!

  int len length length of string

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void id_char_handler(void *ptr_data, const char *string, int len) {
  struct getbib_data* ptr_gbdata;
  char *term_string;
  char *new_string;
  
  ptr_gbdata = (struct getbib_data*)ptr_data;

  if (*(ptr_gbdata->ptr_ndb_error) || *(ptr_gbdata->ptr_nmem_error)) {
    return;
  }

  term_string = malloc(len+1);
  if (!term_string) {
    (*(ptr_gbdata->ptr_nmem_error))++;
    return;
  }

  strncpy(term_string, string, len);
  term_string[len] = '\0';

  if ((new_string = mstrcat((ptr_gbdata->ptr_sfirst)->elvalue, term_string, ptr_gbdata->ptr_cmid_len, 0)) == NULL) {
    free(term_string);
    (*(ptr_gbdata->ptr_nmem_error))++;
    return;
  }
  else {
    (ptr_gbdata->ptr_sfirst)->elvalue = new_string;
    free(term_string);
  }
/*    printf(">>%s\n", ptr_first->elvalue); */
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  idraw_start_handler(): handler for start tags (handling citation/xref
                      data for raw bibliographies)
  
  void idraw_start_handler has no return value

  void* ptr_data this is a ptr to "non-global" global data that all
             handlers share - will be cast to type struct getbib_data*

  const char *el ptr to a string containing the element name

  const char** ptr_attr ptr to an array of attributes (name-value pairs)

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void idraw_start_handler(void *ptr_data, const char *el, const char **ptr_attr) {
  struct simple_elstack* ptr_el_new;
  struct getbib_data* ptr_gbdata;

  ptr_gbdata = (struct getbib_data*)ptr_data;

  /* add current element to element stack */
  ptr_el_new = malloc(sizeof(struct simple_elstack));
  if (ptr_el_new == NULL) {
    (*(ptr_gbdata->ptr_nmem_error))++;
  }
  else {
    /*    printf("have memory\n"); */
    strncpy(ptr_el_new->elname, el, 63);
    ptr_el_new->elname[63] = '\0'; /* terminate just in case */
    ptr_el_new->elvalue = malloc(256); /* something to start with */
    if (ptr_el_new->elvalue == NULL) {
      (*(ptr_gbdata->ptr_nmem_error))++;
    }
    else {
      ptr_el_new->elvalue[0] = '\0';
      ptr_el_new->ptr_snext = ptr_gbdata->ptr_sfirst;
      ptr_gbdata->ptr_sfirst = ptr_el_new;
    }
    /*    printf("%s", ptr_first->elname); */

    /* add attribute */
    if (ptr_attr[0] && ptr_attr[1]) {
      ptr_el_new->attrvalue = mstrdup((char*)ptr_attr[1]);
      if (!ptr_el_new->attrvalue) {
	(*(ptr_gbdata->ptr_nmem_error))++;
      }
    }
    else {
      ptr_el_new->attrvalue = NULL;
    }
  }
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  idraw_end_handler(): handler for end tags (handling citation/xref data
                       for raw bibliographies)

  void idraw_end_handler has no return value

  void* ptr_data this is a ptr to "non-global" global data that all
             handlers share - will be cast to type struct getbib_data*

  const char *el ptr to a string containing the element name

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void idraw_end_handler(void *ptr_data, const char *el) {
  size_t sql_command_len;
  int nis_multi = 0;
  char* database = "";
  char* id_string;
  char* multi_id_string;
  char* multi_database = "";
  char* sql_command;
  char* new_el;
  char* new_elvalue;
  char* new_attrvalue = NULL;
  char* reftype = NULL;
  const char *drivername; /* name of the libdbi driver */
  char* real_orig_citekey = NULL;
  dbi_conn conn_source = NULL;
  dbi_result dbires;
  struct getbib_data* ptr_gbdata;
  struct simple_elstack* ptr_el_remove;

  ptr_gbdata = (struct getbib_data*)ptr_data;

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

  if ((new_el = mstrdup((char*)el)) == NULL) {
    (*(ptr_gbdata->ptr_nmem_error))++;
    return;
  }

  strup(new_el);

  if ((new_elvalue = mstrdup((char*)((ptr_gbdata->ptr_sfirst)->elvalue))) == NULL) {
    (*(ptr_gbdata->ptr_nmem_error))++;
    return;
  }

  if ((ptr_gbdata->ptr_sfirst)->attrvalue && (new_attrvalue = mstrdup((char*)((ptr_gbdata->ptr_sfirst)->attrvalue))) == NULL) {
    (*(ptr_gbdata->ptr_nmem_error))++;
    return;
  }


  if (strcmp(new_el, "XREF") == 0) {
    sql_command_len = 512;
    if ((sql_command = malloc(sql_command_len)) == NULL) {
      (*(ptr_gbdata->ptr_nmem_error))++;
      return;
    }

    id_string = getid((ptr_gbdata->ptr_sfirst)->elvalue, &database, &nis_multi);

    nis_multi = 0; /* protects against malformed linkends */
    if ((ptr_gbdata->ptr_sfirst)->attrvalue) {
      multi_id_string = getid((ptr_gbdata->ptr_sfirst)->attrvalue, &multi_database, &nis_multi);
    }

/*     printf("id_string:%s<< nis_multi:%d<<\n", id_string, nis_multi); */
    if (nis_multi) {
      /* no need to add this one as the next xref contains the same ID */
      return;
    }

    if (database) {
      strdn(database); /* use lowercase name */
    }
    else {
      database = ptr_gbdata->ptr_default_db; /* use default database */
    }

    /* todo: use two lists for ids and citation keys to avoid database
       accesses here (don't care for redundant ID request), or
       retrieve ID of those identified by citation key and use a
       special linked list add command that eliminates duplicates? */

    /* if orig_id is numerical, retrieve the citation key. This
       assumes that most documents will use citation keys anyway, so
       that case will be handled most efficiently */
    if (is_number(id_string)) {
      if (!strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn, "multiple_db"), "t")) {
	sprintf(sql_command, "SELECT refdb_citekey, refdb_type FROM %s.t_refdb WHERE refdb_id='%s' AND refdb_type!='DUMMY'", database, id_string);
	dbires = dbi_conn_query(ptr_gbdata->conn, sql_command);
      }
      else {
	/* get additional connection */
	if ((conn_source = connect_to_db(ptr_gbdata->ptr_clrequest, database, 0)) == NULL) {
	  LOG_PRINT(LOG_ERR, get_status_msg(205));
	  return;
	}
	sprintf(sql_command, "SELECT refdb_citekey, refdb_type FROM t_refdb WHERE refdb_id='%s' AND refdb_type!='DUMMY'", id_string);
	dbires = dbi_conn_query(conn_source, sql_command);
      }
      
      LOG_PRINT(LOG_DEBUG, sql_command);
      
      if (!dbires) {
	LOG_PRINT(LOG_WARNING, "select failed");
	(*(ptr_gbdata->ptr_ndb_error))++;
      }
      else {
	if (dbi_result_next_row(dbires)) {
	  real_orig_citekey = (char*)dbi_result_get_string(dbires, "refdb_citekey");
	  reftype = my_dbi_result_get_string_copy(dbires, "refdb_type");
	}
	dbi_result_free(dbires);
      }

      if (strcmp(my_dbi_conn_get_cap(ptr_gbdata->conn, "multiple_db"), "t") && conn_source) {
	dbi_conn_close(conn_source);
      }
    } /* end if numerical */
    else {
      real_orig_citekey = id_string;
    }
  
    if (real_orig_citekey && *real_orig_citekey) {
      /* see whether this citation key is already in the list */
      if (!find_lilifstring(ptr_gbdata->ptr_found_first, real_orig_citekey)) {
	(*(ptr_gbdata->ptr_xref_count))++;
	/* add ID or citation key to the linked list of unfound stuff */
	if (append_lilifstring(ptr_gbdata->ptr_found_first, real_orig_citekey)) {
	  (*(ptr_gbdata->ptr_ndb_error))++;
	}
      }
    }
    else { /* ID not found */
      /* add ID or citation key to the linked list of unfound stuff */
      if (append_lilifstring(ptr_gbdata->ptr_notfound_first, id_string)) {
	(*(ptr_gbdata->ptr_ndb_error))++;
      }
    }

    free(sql_command);
  }
  else if (strcmp(new_el, "CITATION") == 0) {
    (*(ptr_gbdata->ptr_citation_count))++;
    (*(ptr_gbdata->ptr_xref_count)) = 0;

  } /* end if XREF or CITATION */

  /* remove current element from element stack */
  ptr_el_remove = ptr_gbdata->ptr_sfirst;
  ptr_gbdata->ptr_sfirst = (ptr_gbdata->ptr_sfirst)->ptr_snext;
  free(ptr_el_remove->elvalue);
  if (ptr_el_remove->attrvalue) {
    free(ptr_el_remove->attrvalue);
  }
  free(ptr_el_remove);
  
  free(new_el);
  free(new_elvalue);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  idraw_char_handler(): handler for character data (ID data for raw
                        bibliographies). 

  void idraw_char_handler has no return value

  void* ptr_data this is a ptr to "non-global" global data that all
             handlers share - will be cast to type struct getbib_data*

  const char *string ptr to a string containing the char data
                     this string is not \0 terminated!

  int len length length of string

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void idraw_char_handler(void *ptr_data, const char *string, int len) {
  struct getbib_data* ptr_gbdata;
  char *term_string;
  char *new_string;
  
  ptr_gbdata = (struct getbib_data*)ptr_data;

  if (*(ptr_gbdata->ptr_ndb_error) || *(ptr_gbdata->ptr_nmem_error)) {
    return;
  }

  term_string = malloc(len+1);
  if (!term_string) {
    (*(ptr_gbdata->ptr_nmem_error))++;
    return;
  }

  strncpy(term_string, string, len);
  term_string[len] = '\0';

  if ((new_string = mstrcat((ptr_gbdata->ptr_sfirst)->elvalue, term_string, ptr_gbdata->ptr_cmid_len, 0)) == NULL) {
    free(term_string);
    (*(ptr_gbdata->ptr_nmem_error))++;
    return;
  }
  else {
    (ptr_gbdata->ptr_sfirst)->elvalue = new_string;
    free(term_string);
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  delete_idlist(): deletes the linked element list. This is to free
                   the memory in case of an error

  void delete_idlist

  struct simple_elstack* ptr_current_element ptr to the first entry in the
                  linked list

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void delete_idlist(struct simple_elstack* ptr_current_element) {
  struct simple_elstack* ptr_el;
  struct simple_elstack* ptr_el_next;

  ptr_el = ptr_current_element;

  while (ptr_el != NULL) {
    free(ptr_el->elvalue);
    free(ptr_el->attrvalue);
    ptr_el_next = ptr_el->ptr_snext;
    free(ptr_el);
    ptr_el = ptr_el_next;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  set_alternatetext(): sets the alternatetext/authorempty stuff

  int set_alternatetext returns the type of title to use if no authors
                       are given (1=part, 2=publication, 3=series,
                       4=first available), 0 if no title is to be used
		       -1 in case of an error

  dbi_result dbires   result of a query for the style values

  char** ptr_aempty   ptr to a string which will receive an allocated
                      copy of the AEMPTY string, if any. If there is
                      no such string, the pointer pointed to by this
                      parameter will be set to NULL

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int set_alternatetext(dbi_result dbires, char** ptr_aempty) {
  const char *qaempty;
  const char *xaempty;
  const char *qalternatetext;
  const char *xalternatetext;
  int use_title_as_author = 0;
  dbi_conn conn;

  if (!dbires || !ptr_aempty) {
    return -1;
  }

  *ptr_aempty = NULL;

  conn = dbi_result_get_conn(dbires);

  if (dbi_result_next_row(dbires)) {
    qaempty = my_dbi_result_get_string(dbires, "QAEMPTY");
    xaempty = my_dbi_result_get_string(dbires, "XAEMPTY");
    qalternatetext = my_dbi_result_get_string(dbires, "QALTERNATETEXT");
    xalternatetext = my_dbi_result_get_string(dbires, "XALTERNATETEXT");
/*  	      printf("string went to %s<<\n", string);  */

    /* first check part authors */
    if (qalternatetext) {
      if (!strcmp(qalternatetext, "TITLEPART")) {
	use_title_as_author = 1; /* use part title */
      }
      else if (!strcmp(qalternatetext, "TITLEPUB")) {
	use_title_as_author = 2; /* use publication title */
      }
      else if (!strcmp(qalternatetext, "TITLESERIES")) {
	use_title_as_author = 3; /* use series title */
      }
      else if (!strcmp(qalternatetext, "TITLEALL")) {
	use_title_as_author = 4; /* use first available title */
      }
      else if (!strcmp(qalternatetext, "JOURNALNAME")) {
	use_title_as_author = 5; /* use periodical name */
      }
      else { /* AEMPTY */
	use_title_as_author = 0;

	if (qaempty) {
	  *ptr_aempty = strdup(qaempty);
	  if (!*ptr_aempty || !dbi_conn_quote_string(conn, ptr_aempty)) {
	    LOG_PRINT(LOG_WARNING, "out of memory");
	    return -1;
	  }
	}
      }
    }
    else if (xalternatetext) {
      if (!strcmp(xalternatetext, "TITLEPART")) {
	use_title_as_author = 1; /* use part title */
      }
      else if (!strcmp(xalternatetext, "TITLEPUB")) {
	use_title_as_author = 2; /* use publication title */
      }
      else if (!strcmp(xalternatetext, "TITLESERIES")) {
	use_title_as_author = 3; /* use series title */
      }
      else if (!strcmp(xalternatetext, "TITLEALL")) {
	use_title_as_author = 4; /* use first available title */
      }
      else if (!strcmp(xalternatetext, "JOURNALNAME")) {
	use_title_as_author = 5; /* use periodical name */
      }
      else { /* AEMPTY */
	use_title_as_author = 0;
	
	if (xaempty) {
	  *ptr_aempty = strdup(xaempty);
	  if (!ptr_aempty || !dbi_conn_quote_string(conn, ptr_aempty)) {
	    LOG_PRINT(LOG_WARNING, "out of memory");
	    return -1;
	  }
	}
      }
    }
  } /* end if have row */

  return use_title_as_author;
}
