/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  backend-mods.c defines the MODS output backend of refdbd
  markus@mhoenicka.de 2007-03-13

  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 <syslog.h> /* for definitions of log message priorities */
#include <stdio.h>
#include <dbi/dbi.h>

#include "refdb.h"
#include "linklist.h"
#include "tokenize.h"
#include "backend.h"
#include "backend-mods.h"
#include "strfncs.h"
#include "refdbd.h"
#include "dbfncs.h"
#include "risdb.h"
#include "authorinfo.h"
#include "xmlhelper.h"
#include "connect.h"

extern char refdblib[]; /* location of shareable files */

extern int n_log_level; /* numeric version of log_level */

/* forward declaration of local functions */
static char* add_authors_mods(char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, int type, struct renderinfo* ptr_rendinfo, int* nhave_buffer_data, struct xmlindent* ptr_indent);
static char* add_type_mods(char* ristype, char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, struct renderinfo* ptr_rendinfo, int* nhave_buffer_data, struct xmlindent* ptr_indent);
static char* add_origininfo_mods(char* ristype, int level, char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, struct renderinfo* ptr_rendinfo, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent);
static char* add_partinfo_mods(char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, struct renderinfo* ptr_rendinfo, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent);
static char* add_location_mods(char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, struct renderinfo* ptr_rendinfo, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent);
static char* add_pubdate_mods(char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, int type, struct renderinfo* ptr_rendinfo, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent);
static char* add_reprint_mods(char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, struct renderinfo* ptr_rendinfo, char* username, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent);
static int indent_notbelow_mods(const char* name);
static int is_entry_mods(const char* name);

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  prepare_render_mods(): writes a header for the mods output of a
  query

  int prepare_render_mods returns 0 if successful, > 0 if failed

  struct renderinfo* ptr_rendinfo ptr to a structure with the info
  how the reference should be rendered

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int prepare_render_mods(struct renderinfo* ptr_rendinfo) {
  char* new_ref;
  char* header;
  char ns[PREFS_BUF_LEN+128];
  
  if ((header = assemble_header(ptr_rendinfo)) == NULL) {
    return 801;
  }

  new_ref = mstrcpy(*(ptr_rendinfo->ptr_ref), header, ptr_rendinfo->ptr_ref_len);
  free(header);

  if (new_ref == NULL) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 801;
  }
  else {
    *(ptr_rendinfo->ptr_ref) = new_ref;
  }

  if (*(ptr_rendinfo->ptr_clrequest->namespace)) {
    sprintf(ns, "<%s:modsCollection xmlns:%s=\"http://www.loc.gov/mods/v3\">\n", ptr_rendinfo->ptr_clrequest->namespace, ptr_rendinfo->ptr_clrequest->namespace);
  }
  else {
    strcpy(ns, "<modsCollection>\n");
  }

  new_ref = mstrcat(*(ptr_rendinfo->ptr_ref), ns, ptr_rendinfo->ptr_ref_len, 0);

  if (new_ref == NULL) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 801;
  }

  *(ptr_rendinfo->ptr_ref) = new_ref;

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  finish_render_mods(): writes a footer for the mods output of a query

  int finish_render_mods returns 0 if successful,>0 if failed

  struct renderinfo* ptr_rendinfo ptr to a structure with the info
  how the reference should be rendered

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int finish_render_mods(struct renderinfo* ptr_rendinfo) {
  char* new_ref;
  char ns[PREFS_BUF_LEN+32];

  if (*(ptr_rendinfo->ptr_clrequest->namespace)) {
    sprintf(ns, "\n</%s:modsCollection>\n", ptr_rendinfo->ptr_clrequest->namespace);
  }
  else {
    strcpy(ns, "\n</modsCollection>\n");
  }

  new_ref = mstrcpy(*(ptr_rendinfo->ptr_ref), ns, ptr_rendinfo->ptr_ref_len);

  if (new_ref == NULL) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 801;
  }

  *(ptr_rendinfo->ptr_ref) = new_ref;

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  render_mods() renders a RIS dataset for MODS export

  int render_mods returns 0 if successful, >0 if failed

  struct renderinfo* ptr_rendinfo ptr to a structure with the info
  how the reference should be rendered

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int render_mods(struct renderinfo* ptr_rendinfo) {
  int errcode; /* receives error code for periodical requests */
  int nhave_buffer_data = 0; /* 1 if buffer contains real data */
  int nhave_analytic = 0; /* 1 if reference has analytical data */
  size_t buffer_len;
  const char* item;
  const char* item1;
  char id[32] = "";
  char type[7] = "JOUR";
  char empty_string[1] = "";
  char date_buffer[256];
  char* new_ref;
  char* buffer;
  dbi_result dbires = NULL;
  dbi_conn conn;
  struct xmlindent xindent;

  /*initialize xindent */
  initialize_xmlindent(&xindent, 2, indent_notbelow_mods, is_entry_mods);

  conn = dbi_result_get_conn(ptr_rendinfo->dbires);

  if (get_refdb_id(ptr_rendinfo->dbires, id) == NULL) {
    LOG_PRINT(LOG_WARNING, get_status_msg(234));
    return 234;
  }

  /*----------------------------------------------------------------*/
  /* start mods entry with the citekey attribute */
  item = get_refdb_type(ptr_rendinfo->dbires);

  if (item && *item) {
    strncpy(type, item, 6);
    type[6] = '\0';
  }

  item1 = get_refdb_citekey(ptr_rendinfo->dbires);

  if (print_elstart_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "mods", "ID", item1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    return 801;
  }

  /* add the RIS type for debugging purposes */
  sprintf(date_buffer, "<!-- RIS type: %s -->\n", type);
  if ((new_ref = mstrcat(*(ptr_rendinfo->ptr_ref), date_buffer, ptr_rendinfo->ptr_ref_len, 0)) == NULL) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    free(buffer);
    return 801;
  }
  else {
    *(ptr_rendinfo->ptr_ref) = new_ref;
  }

  /* get some memory for the part/publication/set stuff */
  buffer_len = 4096;
  if ((buffer = malloc(buffer_len)) == NULL) {
    LOG_PRINT(LOG_CRIT, get_status_msg(801));
    return 801;
  }
  *buffer = '\0';

  /*----------------------------------------------------------------*/
  /* the part apparatus (analytic) */

  /* only these types are supposed to have analytic information */
  if (has_part_data(type)) {

    /* part title */
    item = get_refdb_title_copy(ptr_rendinfo->dbires);
    if (item != NULL) {
      if (print_elstart_x(&buffer, &buffer_len, "titleInfo", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer);
	return 801;
      }

      if (print_element_x(item, &buffer, &buffer_len, "title", NULL, NULL, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer);
	free((char*)item);
	return 801;
      }
      free((char*)item);
    }
    else {
      if (print_element_x(empty_string, &buffer, &buffer_len, "title", NULL, NULL, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer);
	free((char*)item);
	return 801;
      }
    }

    if (print_elend_x(&buffer, &buffer_len, "titleInfo", &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer);
      return 801;
    }
    nhave_buffer_data = 1;


    if (add_authors_mods(&buffer, &buffer_len, conn, 1 /* part */, ptr_rendinfo, &nhave_buffer_data, &xindent) == NULL) {
      free(buffer);
      return 801;
    }
    
    /* type */
    if (add_type_mods(type, &buffer, &buffer_len, conn, ptr_rendinfo, &nhave_buffer_data, &xindent) == NULL) {
      free(buffer);
      return 801;
    }

    if (add_origininfo_mods(type, 0 /* monographic */, &buffer, &buffer_len, conn, ptr_rendinfo, &nhave_buffer_data, &xindent) == NULL) {
      free(buffer);
      return 801;
    }

    /* write out part info */
    if (nhave_buffer_data) {
      if ((new_ref = mstrcat(*(ptr_rendinfo->ptr_ref), buffer, ptr_rendinfo->ptr_ref_len, 0)) == NULL) {
	LOG_PRINT(LOG_WARNING, get_status_msg(801));
	free(buffer);
	return 801;
      }
      else {
	*(ptr_rendinfo->ptr_ref) = new_ref;
      }
    }
    nhave_analytic++;
  }


  /*----------------------------------------------------------------*/
  /* the publication apparatus (monographic) */
  nhave_buffer_data = 0;
  *buffer = '\0';

  if (nhave_analytic) {
    if (print_elstart_x(&buffer, &buffer_len, "relatedItem", "type", "host", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    free(buffer);
    return 801;
    }
  }

  if (has_periodical_data(type)) {
    /*----------------------------------------------------------------*/
    /* journal (full)*/
    if ((item = get_periodical(conn, date_buffer, NULL, 0, &errcode, 0, my_dbi_result_get_idval(ptr_rendinfo->dbires, "refdb_id"), NULL /* no frequency required */)) != NULL && *item) {
      nhave_buffer_data = 1;

      if (print_elstart_x(&buffer, &buffer_len, "titleInfo", "type", "uniform", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer);
	return 801;
      }

      if (print_element_x(item, &buffer, &buffer_len, "title", NULL, NULL, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer);
	return 801;
      }

      if (print_elend_x(&buffer, &buffer_len, "titleInfo", &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer);
	return 801;
      }
    }
    
    /*----------------------------------------------------------------*/
    /* journal (abbrev)*/
    if ((item = get_periodical(conn, date_buffer, NULL, 3, &errcode, 0, my_dbi_result_get_idval(ptr_rendinfo->dbires, "refdb_id"), NULL /* no frequency required */)) != NULL && *item) {
      nhave_buffer_data = 1;

      if (print_elstart_x(&buffer, &buffer_len, "titleInfo", "type", "abbreviated", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer);
	return 801;
      }

      if (print_element_x(item, &buffer, &buffer_len, "title", NULL, NULL, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer);
	return 801;
      }

      if (print_elend_x(&buffer, &buffer_len, "titleInfo", &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer);
	return 801;
      }
    }
  }
  
  item = get_refdb_booktitle_copy(ptr_rendinfo->dbires);

  if (item != NULL) {
    if (print_elstart_x(&buffer, &buffer_len, "titleInfo", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer);
      return 801;
    }

    if (print_element_x(item, &buffer, &buffer_len, "title", NULL, NULL, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer);
      free((char*)item);
      return 801;
    }
    free((char*)item);

    if (print_elend_x(&buffer, &buffer_len, "titleInfo", &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer);
      return 801;
    }
  }

  if (add_authors_mods(&buffer, &buffer_len, conn, 2 /* publication */, ptr_rendinfo, &nhave_buffer_data, &xindent) == NULL) {
    free(buffer);
    return 801;
  }

  if (add_origininfo_mods(type, 1 /* monographic */, &buffer, &buffer_len, conn, ptr_rendinfo, &nhave_buffer_data, &xindent) == NULL) {
    free(buffer);
    return 801;
  }

  if (add_partinfo_mods(&buffer, &buffer_len, conn, ptr_rendinfo, &nhave_buffer_data, &xindent) == NULL) {
    free(buffer);
    return 801;
  }

  if (nhave_buffer_data) {
    if ((new_ref = mstrcat(*(ptr_rendinfo->ptr_ref), buffer, ptr_rendinfo->ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(buffer);
      return 801;
    }
    else {
      *(ptr_rendinfo->ptr_ref) = new_ref;
    }
  }

  /*----------------------------------------------------------------*/
  /* the set apparatus (series) */
  nhave_buffer_data = 0;
  *buffer = '\0';

  if (print_elstart_x(&buffer, &buffer_len, "relatedItem", "type", "host", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    free(buffer);
    return 801;
  }

  /* set title */
  if ((item = get_refdb_title_series_copy(ptr_rendinfo->dbires)) != NULL) {
    nhave_buffer_data = 1;

    if (print_elstart_x(&buffer, &buffer_len, "titleInfo", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer);
      return 801;
    }

    if (print_element_x(item, &buffer, &buffer_len, "title", NULL, NULL, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free((char*)item);
      return 801;
    }
    free((char*)item);

    if (print_elend_x(&buffer, &buffer_len, "titleInfo", &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer);
      return 801;
    }
  }

  if (add_authors_mods(&buffer, &buffer_len, conn, 3 /* set */, ptr_rendinfo, &nhave_buffer_data, &xindent) == NULL) {
    free(buffer);
    return 801;
  }

  if (print_elend_x(&buffer, &buffer_len, "relatedItem", &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    free(buffer);
    return 801;
  }

  if (nhave_buffer_data) {
    if ((new_ref = mstrcat(*(ptr_rendinfo->ptr_ref), buffer, ptr_rendinfo->ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(buffer);
      return 801;
    }
    else {
      *(ptr_rendinfo->ptr_ref) = new_ref;
    }
  }

  nhave_buffer_data = 0;
  *buffer = '\0';

  if (nhave_analytic) {
    if (print_elend_x(&buffer, &buffer_len, "relatedItem", &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer);
      return 801;
    }
    nhave_buffer_data = 1;
  }

  /*----------------------------------------------------------------*/
  /* ISBN/ISSN */
  if ((item = get_refdb_issn(ptr_rendinfo->dbires)) != NULL) {
    nhave_buffer_data = 1;
    if (has_periodical_data(type)) {
      if (print_element_x(item, &buffer, &buffer_len, "identifier", "type", "issn", NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer);
	return 801;
      }
    }
    else {
      if (print_element_x(item, &buffer, &buffer_len, "identifier", "type", "isbn", NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer);
	return 801;
      }
    }
  }


  if (add_location_mods(&buffer, &buffer_len, conn, ptr_rendinfo, &nhave_buffer_data, &xindent) == NULL) {
    free(buffer);
    return 801;
  }

  if (nhave_buffer_data) {
    if ((new_ref = mstrcat(*(ptr_rendinfo->ptr_ref), buffer, ptr_rendinfo->ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(buffer);
      return 801;
    }
    else {
      *(ptr_rendinfo->ptr_ref) = new_ref;
    }
  }



  nhave_buffer_data = 0;
  *buffer = '\0';

  /*----------------------------------------------------------------*/
  /* abstract */
  if ((item = get_refdb_abstract_copy(ptr_rendinfo->dbires)) != NULL) {
    nhave_buffer_data = 1;

    if (print_element_x(item, &buffer, &buffer_len, "abstract", NULL, NULL, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free((char*)item);
      free(buffer);
      return 801;
    }
    free((char*)item);
  }

  /*----------------------------------------------------------------*/
  /* notes */
  if ((item = get_notes_copy(ptr_rendinfo->dbires, ptr_rendinfo->username)) != NULL) {
    nhave_buffer_data = 1;
    
    if (print_element_x(item, &buffer, &buffer_len, "notes", NULL, NULL, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer);
      free((char*)item);
      clean_request(dbires);
      return 801;
    }
    free((char*)item);
  }
    
  /*----------------------------------------------------------------*/
  /* keywords */
  dbires = request_keywords(conn, my_dbi_result_get_idval(ptr_rendinfo->dbires, "refdb_id"), 0, 0);
  if (dbires == NULL) {
    return 234;
  }

  while ((item = get_keyword(dbires)) != NULL) {
    nhave_buffer_data = 1;

    if (print_elstart_x(&buffer, &buffer_len, "subject", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer);
      return 801;
    }

    if (print_element_x(item, &buffer, &buffer_len, "topic", NULL, NULL, NULL, NULL, &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer);
      clean_request(dbires);
      return 801;
    }
    
    if (print_elend_x(&buffer, &buffer_len, "subject", &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer);
      return 801;
    }
  }
  clean_request(dbires);

  if (nhave_buffer_data) {
    if ((new_ref = mstrcat(*(ptr_rendinfo->ptr_ref), buffer, ptr_rendinfo->ptr_ref_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(buffer);
      return 801;
    }
    else {
      *(ptr_rendinfo->ptr_ref) = new_ref;
    }
  }

  free(buffer);

  /*----------------------------------------------------------------*/
  /* The End */

  if (print_elend_x(ptr_rendinfo->ptr_ref, ptr_rendinfo->ptr_ref_len, "mods", &xindent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    return 801;
  }

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  add_authors_mods() prints out mods authors

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

  char** ptr_buffer ptr to ptr to buffer that will receive the output

  size_t* ptr_buffer_len ptr to var holding size of *ptr_buffer

  dbi_conn conn connection to database

  int type 1=part author, 2=publication author, 3=series editor

  struct renderinfo* ptr_renderinfo ptr to struct with render information

  int* ptr_nhave_buffer_data ptr to var which will be set to non-zero
                             if *ptr_buffer receives data

  struct xmlindent* ptr_indent ptr to struct with indent info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* add_authors_mods(char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, int type, struct renderinfo* ptr_rendinfo, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent) {
  dbi_result dbires;
  char* entitize_buf;
  struct AUTHOR_INFO* ptr_ainfo;

  if ((ptr_ainfo = new_authorinfo()) == NULL) {
    return NULL;
  }

  dbires = request_authors(conn, type, NULL /* all roles */,  NULL, 0, my_dbi_result_get_idval(ptr_rendinfo->dbires, "refdb_id"));
  if (dbires == NULL) {
    free_authorinfo(ptr_ainfo);
    return NULL;
  }

  /* fetch author parts */
  while (get_author_parts(dbires, ptr_ainfo)) {
    char nametype[11]; /* enough to hold 'conference' */

    if (!*(ptr_ainfo->lastname) && !*(ptr_ainfo->firstname)
	&& !*(ptr_ainfo->middlename) && !*(ptr_ainfo->suffix)) {
      strcpy(nametype, "corporate");
    }
    else {
      strcpy(nametype, "personal");
    }

    if (print_elstart_x(ptr_buffer, ptr_buffer_len, "name", "type", nametype, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      clean_request(dbires);
      free_authorinfo(ptr_ainfo);
      return NULL;
    }

    /* see whether we've got parts or only one author string */
    if (!*(ptr_ainfo->lastname) && !*(ptr_ainfo->firstname)
	&& !*(ptr_ainfo->middlename) && !*(ptr_ainfo->suffix)) {
      if ((entitize_buf = mstrdup(ptr_ainfo->name)) == NULL) {
	clean_request(dbires);
	free_authorinfo(ptr_ainfo);
	return NULL;
      }

      if (sgml_entitize(&entitize_buf, ptr_rendinfo->ref_format) == NULL) {
	clean_request(dbires);
	free(entitize_buf);
	free_authorinfo(ptr_ainfo);
	return NULL;
      }

      if (print_element_x(entitize_buf, ptr_buffer, ptr_buffer_len, "namePart", NULL, NULL, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	clean_request(dbires);
	free(entitize_buf);
	free_authorinfo(ptr_ainfo);
	return NULL;
      }
      free(entitize_buf);
    }
    else { /* if have nameparts */
      if (*(ptr_ainfo->lastname)) {
	*ptr_nhave_buffer_data = 1;
	if ((entitize_buf = mstrdup(ptr_ainfo->lastname)) == NULL) {
	  clean_request(dbires);
	  free_authorinfo(ptr_ainfo);
	  return NULL;
	}
	  
	if (sgml_entitize(&entitize_buf, ptr_rendinfo->ref_format) == NULL) {
	  free(entitize_buf);
	  free_authorinfo(ptr_ainfo);
	  return NULL;
	}

	if (print_element_x(entitize_buf, ptr_buffer, ptr_buffer_len, "namePart", "type", "family", NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	  clean_request(dbires);
	  free(entitize_buf);
	  free_authorinfo(ptr_ainfo);
	  return NULL;
	}
	free(entitize_buf);
      }
	
      if (*(ptr_ainfo->firstname)) {
	char full_firstname[514]; /* some extra for space and \0 */

	if (*(ptr_ainfo->middlename)) {
	  snprintf(full_firstname, 514, "%s %s", ptr_ainfo->firstname, ptr_ainfo->middlename);
	}
	else {
	  strncpy(full_firstname, ptr_ainfo->firstname, 514);
	}

	*ptr_nhave_buffer_data = 1;
	if ((entitize_buf = mstrdup(full_firstname)) == NULL) {
	  clean_request(dbires);
	  free_authorinfo(ptr_ainfo);
	  return NULL;
	}
      
	if (sgml_entitize(&entitize_buf, ptr_rendinfo->ref_format) == NULL) {
	  free(entitize_buf);
	  free_authorinfo(ptr_ainfo);
	  return NULL;
	}
	
	/* MODS does not distinguish between first and middle names. We have to add all middle names to the firstname */
	if (print_element_x(entitize_buf, ptr_buffer, ptr_buffer_len, "namePart", "type", "given", NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	  clean_request(dbires);
	  free_authorinfo(ptr_ainfo);
	  return NULL;
	}
	free(entitize_buf);
      }


/*       if (*(ptr_ainfo->suffix)) { */
/* 	*ptr_nhave_buffer_data = 1; */
/* 	if ((entitize_buf = mstrdup(ptr_ainfo->suffix)) == NULL) { */
/* 	  clean_request(dbires); */
/* 	  free_authorinfo(ptr_ainfo); */
/* 	  return NULL; */
/* 	} */
	  
/* 	if (sgml_entitize(&entitize_buf, NULL) == NULL) { */
/* 	  free(entitize_buf); */
/* 	  free_authorinfo(ptr_ainfo); */
/* 	  return NULL; */
/* 	} */
	  
/* 	if (print_element_x(entitize_buf, ptr_buffer, ptr_buffer_len, "suffix", NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) { */
/* 	  clean_request(dbires); */
/* 	  free_authorinfo(ptr_ainfo); */
/* 	  return NULL; */
/* 	} */
/* 	free(entitize_buf); */
/*       } */
    }
	
    /* author role */
      if (print_elstart_x(ptr_buffer, ptr_buffer_len, "role", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	clean_request(dbires);
	free_authorinfo(ptr_ainfo);
	return NULL;
      }
      
    if (*(ptr_ainfo->role)) {
      if ((entitize_buf = mstrdup(ptr_ainfo->role)) == NULL) {
	clean_request(dbires);
	free_authorinfo(ptr_ainfo);
	return NULL;
      }
      
      if (sgml_entitize(&entitize_buf, ptr_rendinfo->ref_format) == NULL) {
	free(entitize_buf);
	free_authorinfo(ptr_ainfo);
	return NULL;
      }
    }
    else {
      switch (type) {
      case 1:
	entitize_buf = strdup("author");
	break;
      case 2:
	entitize_buf = strdup("editor");
	break;
      case 3:
	entitize_buf = strdup("series editor");
	break;
      }
    }

    if (print_element_x(entitize_buf, ptr_buffer, ptr_buffer_len, "roleTerm", "type", "text", NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      clean_request(dbires);
      free_authorinfo(ptr_ainfo);
      free(entitize_buf);
      return NULL;
    }

    free(entitize_buf);

    if (print_elend_x(ptr_buffer, ptr_buffer_len, "role", ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      clean_request(dbires);
      free_authorinfo(ptr_ainfo);
      return NULL;
    }

    if (print_elend_x(ptr_buffer, ptr_buffer_len, "name", ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      clean_request(dbires);
      free_authorinfo(ptr_ainfo);
      return NULL;
    }
  } /* end while */

  
  clean_request(dbires);
  free_authorinfo(ptr_ainfo);
  return *ptr_buffer;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  add_type_mods() prints out mods resource type

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

  char* ristype ptr to RIS type

  char** ptr_buffer ptr to ptr to buffer that will receive the output

  size_t* ptr_buffer_len ptr to var holding size of *ptr_buffer

  dbi_conn conn connection to database

  struct renderinfo* ptr_renderinfo ptr to struct with render information

  int* ptr_nhave_buffer_data ptr to var which will be set to non-zero
                             if *ptr_buffer receives data

  struct xmlindent* ptr_indent ptr to struct with indent info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* add_type_mods(char* ristype, char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, struct renderinfo* ptr_rendinfo, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent) {
  char buf[25];

  if (!strcmp(ristype, "SOUND")) {
    strcpy(buf, "sound recording");
  }
  else if (!strcmp(ristype, "VIDEO")
	   || !strcmp(ristype, "MPCT")) {
    strcpy(buf, "moving image");
  }
  else if (!strcmp(ristype, "SLIDE")) {
    strcpy(buf, "still image");
  }
  else if (!strcmp(ristype, "ART")) {
    strcpy(buf, "three dimensional object");
  }
  else if (!strcmp(ristype, "COMP")
	   || !strcmp(ristype, "DATA")) {
    strcpy(buf, "software, multimedia");
  }
  else if (!strcmp(ristype, "MAP")) {
    strcpy(buf, "cartographic");
  }
  else {
    strcpy(buf, "text");
  }

  if (print_element_x(buf, ptr_buffer, ptr_buffer_len, "typeOfResource", NULL, NULL, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    return NULL;
  }

  return *ptr_buffer;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  add_origininfo_mods() prints out mods origin info

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

  char* ristype ptr to RIS type

  int level  0=analytic|1=monographic|2=series

  char** ptr_buffer ptr to ptr to buffer that will receive the output

  size_t* ptr_buffer_len ptr to var holding size of *ptr_buffer

  dbi_conn conn connection to database

  struct renderinfo* ptr_renderinfo ptr to struct with render information

  int* ptr_nhave_buffer_data ptr to var which will be set to non-zero
                             if *ptr_buffer receives data

  struct xmlindent* ptr_indent ptr to struct with indent info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* add_origininfo_mods(char* ristype, int level, char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, struct renderinfo* ptr_rendinfo, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent) {
  char* buffer1;
  char* new_ref;
  const char* item;
  size_t buffer1_len;
  int nhave_data = 0;

/*   printf("in add_origininfo_mods\n"); */
  /* get some memory for the origininfo stuff */
  buffer1_len = 4096;
  if ((buffer1 = malloc(buffer1_len)) == NULL) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return NULL;
  }
  *buffer1 = '\0';

  if (print_elstart_x(&buffer1, &buffer1_len, "originInfo", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    free(buffer1);
    return NULL;
  }

  if (level == 1) {
    /*----------------------------------------------------------------*/
    /* city */
    if ((item = get_refdb_city(ptr_rendinfo->dbires)) != NULL) {
      nhave_data = 1;
      if (print_elstart_x(&buffer1, &buffer1_len, "place", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer1);
	return NULL;
      }
      
      if (print_element_x(item, &buffer1, &buffer1_len, "placeTerm", "type", "text", NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer1);
	return NULL;
      }
      
      if (print_elend_x(&buffer1, &buffer1_len, "place", ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer1);
	return NULL;
      }
    }
    
    /*----------------------------------------------------------------*/
    /* publisher */
    if ((item = get_refdb_publisher(ptr_rendinfo->dbires)) != NULL) {
      nhave_data = 1;
      if (print_element_x(item, &buffer1, &buffer1_len, "publisher", NULL, NULL, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer1);
	return NULL;
      }
    }
    
    /*----------------------------------------------------------------*/
    /* date */
    if (add_pubdate_mods(&buffer1, &buffer1_len, conn, 1 /* primary */, ptr_rendinfo, &nhave_data, ptr_indent) == NULL) {
      free(buffer1);
      return NULL;
    }
    else {
      nhave_data = 1;
    }
  }

  /*----------------------------------------------------------------*/
  /* issuance */

  if (level == 0) {
    if (print_element_x("monographic", &buffer1, &buffer1_len, "issuance", "type", "text", NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }
  }
  else if (level == 1
	   && (!strcmp(ristype, "ABST")
	       || !strcmp(ristype, "JFULL")
	       || !strcmp(ristype, "JOUR")
	       || !strcmp(ristype, "MGZN")
	       || !strcmp(ristype, "NEWS"))) {
    if (print_element_x("continuing", &buffer1, &buffer1_len, "issuance", "type", "text", NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }
  }
  else {
    if (print_element_x("monographic", &buffer1, &buffer1_len, "issuance", "type", "text", NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }
  }
      
  if (print_elend_x(&buffer1, &buffer1_len, "originInfo", ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    free(buffer1);
    return NULL;
  }

  if (nhave_data) {
    /* promote have_data upstream */
    *ptr_nhave_buffer_data = 1;
    if ((new_ref = mstrcat(*ptr_buffer, buffer1, ptr_buffer_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(buffer1);
      return NULL;
    }
    else {
      *ptr_buffer = new_ref;
    }
  }
  free(buffer1);

  return *ptr_buffer;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  add_partinfo_mods() prints out mods part info

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

  char** ptr_buffer ptr to ptr to buffer that will receive the output

  size_t* ptr_buffer_len ptr to var holding size of *ptr_buffer

  dbi_conn conn connection to database

  struct renderinfo* ptr_renderinfo ptr to struct with render information

  int* ptr_nhave_buffer_data ptr to var which will be set to non-zero
                             if *ptr_buffer receives data

  struct xmlindent* ptr_indent ptr to struct with indent info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* add_partinfo_mods(char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, struct renderinfo* ptr_rendinfo, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent) {
  char date_buffer[256] = "";
  char* buffer1;
  char* new_ref;
  const char* item;
  size_t buffer1_len;
  int nhave_data = 0;
  int nhave_year = 0;
  int i;

/*   printf("in add_pubinfo_mods\n"); */
  /* get some memory for the pubinfo stuff */
  buffer1_len = 4096;
  if ((buffer1 = malloc(buffer1_len)) == NULL) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return NULL;
  }
  *buffer1 = '\0';

  if (print_elstart_x(&buffer1, &buffer1_len, "part", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    free(buffer1);
    return NULL;
  }

  /*----------------------------------------------------------------*/
  /* volume */
  if ((item = get_refdb_volume(ptr_rendinfo->dbires)) != NULL) {
    nhave_data = 1;

    if (print_elstart_x(&buffer1, &buffer1_len, "detail", "type", "volume", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }

    if (print_element_x(item, &buffer1, &buffer1_len, "number", NULL, NULL, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }
    
    if (print_elend_x(&buffer1, &buffer1_len, "detail", ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }
  }

  /*----------------------------------------------------------------*/
  /* issue */
  if ((item = get_refdb_issue(ptr_rendinfo->dbires)) != NULL) {
    nhave_data = 1;

    if (print_elstart_x(&buffer1, &buffer1_len, "detail", "type", "issue", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }

    if (print_element_x(item, &buffer1, &buffer1_len, "number", NULL, NULL, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }
    
    if (print_elend_x(&buffer1, &buffer1_len, "detail", ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }
  }

  /*----------------------------------------------------------------*/
  /* start and end page */
  if ((item = get_refdb_startpage(ptr_rendinfo->dbires)) != NULL) {
    nhave_data = 1;

    if (print_elstart_x(&buffer1, &buffer1_len, "extent", "unit", "page", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }

    if (print_element_x(item, &buffer1, &buffer1_len, "start", NULL, NULL, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }
    
    if ((item = get_refdb_endpage(ptr_rendinfo->dbires)) != NULL) {
      if (print_element_x(item, &buffer1, &buffer1_len, "end", NULL, NULL, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	free(buffer1);
	return NULL;
      }
    }

    if (print_elend_x(&buffer1, &buffer1_len, "extent", ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }
  }


  /*----------------------------------------------------------------*/
  /* date */

  if (get_refdb_pubyear(ptr_rendinfo->dbires, date_buffer) != NULL
      && strcmp(date_buffer, "nd")) {
    nhave_year = 1;
  }

  if (nhave_year) {
/*     printf("nhave_year=%d || nhave_other+%d went to TRUE\n", nhave_year, nhave_other); */
    nhave_data++;
    if (print_element_x(date_buffer, &buffer1, &buffer1_len, "date", NULL, NULL, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }
  }

  /*----------------------------------------------------------------*/
  /* URL, L1 through L4, DOI */

  /* loop over all link types */
  for (i=0; i<6;i++) {
    char ulink_type[10];
    dbi_result dbires;

    dbires = request_ulinks(conn, my_dbi_result_get_idval(ptr_rendinfo->dbires, "refdb_id"), 0 /* ref entry */, i /* link type */, 0 /* is_temp */, ptr_rendinfo->username);
    if (dbires == NULL) {
      return NULL;
    }

    while ((item = get_ulink(dbires)) != NULL) {
      char* full_link;

      if (i>0 && i<5) {
	full_link = add_root_to_link(item, ptr_rendinfo->pdfroot);
      }
      else {
	full_link = (char*)item;
      }

      if (i == 5) { /* render doi as identifier */
	if (print_element_x(item, &buffer1, &buffer1_len, "identifier", "type", "doi", NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	  clean_request(dbires);
	  return NULL;
	}
	nhave_data++;
      }
       /* render other links as location */
      else if (i != 3) { 
	if (print_elstart_x(&buffer1, &buffer1_len, "location", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	  free(buffer1);
	  return NULL;
	}

	if (print_element_x((const char*)full_link, &buffer1, &buffer1_len, "url", "access", "raw object", NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	  clean_request(dbires);
	  return NULL;
	}
	nhave_data++;

	if (print_elend_x(&buffer1, &buffer1_len, "location", ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	  free(buffer1);
	  return NULL;
	}
      }
      else if (i == 3) { /* related item */
	if (print_elstart_x(&buffer1, &buffer1_len, "location", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	  free(buffer1);
	  return NULL;
	}

	if (print_element_x((const char*)full_link, &buffer1, &buffer1_len, "note", "related", ulink_type, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	  clean_request(dbires);
	  return NULL;
	}
	nhave_data++;

	if (print_elend_x(&buffer1, &buffer1_len, "location", ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	  free(buffer1);
	  return NULL;
	}
      }

      if (i>0 && i<5) {
	free(full_link);
      }
    }

    clean_request(dbires);
  } /* end for */

  if (print_elend_x(&buffer1, &buffer1_len, "part", ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    free(buffer1);
    return NULL;
  }

  if (nhave_data) {
    /* promote have_data upstream */
    *ptr_nhave_buffer_data = 1;
    if ((new_ref = mstrcat(*ptr_buffer, buffer1, ptr_buffer_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(buffer1);
      return NULL;
    }
    else {
      *ptr_buffer = new_ref;
    }
  }
  free(buffer1);

  return *ptr_buffer;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  add_location_mods() prints out mods location info

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

  char** ptr_buffer ptr to ptr to buffer that will receive the output

  size_t* ptr_buffer_len ptr to var holding size of *ptr_buffer

  dbi_conn conn connection to database

  struct renderinfo* ptr_renderinfo ptr to struct with render information

  int* ptr_nhave_buffer_data ptr to var which will be set to non-zero
                             if *ptr_buffer receives data

  struct xmlindent* ptr_indent ptr to struct with indent info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* add_location_mods(char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, struct renderinfo* ptr_rendinfo, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent) {
  char* buffer1;
  char* new_ref;
  const char* item;
  size_t buffer1_len;
  int nhave_data = 0;
  int i;

/*   printf("in add_pubinfo_mods\n"); */
  /* get some memory for the pubinfo stuff */
  buffer1_len = 4096;
  if ((buffer1 = malloc(buffer1_len)) == NULL) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return NULL;
  }
  *buffer1 = '\0';

  if (print_elstart_x(&buffer1, &buffer1_len, "location", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    free(buffer1);
    return NULL;
  }

  /* reprint stuff */
  if (add_reprint_mods(&buffer1, &buffer1_len, conn, ptr_rendinfo, ptr_rendinfo->username, &nhave_data, ptr_indent) == NULL) {
    free(buffer1);
    return NULL;
  }


  /*----------------------------------------------------------------*/
  /* URL, L1 through L4 */

  /* loop over all link types */
  for (i=0; i<5;i++) {
    char ulink_type[10];
    dbi_result dbires;

    dbires = request_ulinks(conn, my_dbi_result_get_idval(ptr_rendinfo->dbires, "refdb_id"), 0 /* ref entry */, i /* link type */, 0 /* is_temp */, ptr_rendinfo->username);
    if (dbires == NULL) {
      return NULL;
    }

    while ((item = get_ulink(dbires)) != NULL) {
      if (i == 0) {
	strcpy(ulink_type, "url");
      }
      else if (i == 1) {
	strcpy(ulink_type, "pdf");
      }
      else if (i == 2) {
	strcpy(ulink_type, "fulltext");
      }
      else if (i == 3) {
	strcpy(ulink_type, "related");
      }
      else if (i == 4) {
	strcpy(ulink_type, "image");
      }
      if (print_element_x(item, &buffer1, &buffer1_len, "url", "note", ulink_type, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	clean_request(dbires);
	return NULL;
      }
      nhave_data++;
    }

    clean_request(dbires);
  } /* end for */

  if (print_elend_x(&buffer1, &buffer1_len, "location", ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
    free(buffer1);
    return NULL;
  }

  if (nhave_data) {
    /* promote have_data upstream */
    *ptr_nhave_buffer_data = 1;
    if ((new_ref = mstrcat(*ptr_buffer, buffer1, ptr_buffer_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(buffer1);
      return NULL;
    }
    else {
      *ptr_buffer = new_ref;
    }
  }
  free(buffer1);

  return *ptr_buffer;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  add_pubdate_mods() prints out mods pubdate

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

  char** ptr_buffer ptr to ptr to buffer that will receive the output

  size_t* ptr_buffer_len ptr to var holding size of *ptr_buffer

  dbi_conn conn connection to database

  int type 1=primary, 2=secondary

  struct renderinfo* ptr_renderinfo ptr to struct with render information

  int* ptr_nhave_buffer_data ptr to var which will be set to non-zero
                             if *ptr_buffer receives data

  struct xmlindent* ptr_indent ptr to struct with indent info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* add_pubdate_mods(char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, int type, struct renderinfo* ptr_rendinfo, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent) {
  char date_buffer[256] = "";
  char* buffer1;
  char* new_ref;
  size_t buffer1_len;
  int nhave_data = 0;
  int nhave_year = 0;

/*   printf("in add_pubdate_mods\n"); */
  /* get some memory for the pubinfo stuff */
  buffer1_len = 4096;
  if ((buffer1 = malloc(buffer1_len)) == NULL) {
    LOG_PRINT(LOG_WARNING, get_status_msg(801));
    return NULL;
  }
  *buffer1 = '\0';

  /*----------------------------------------------------------------*/
  /* pubyear */
  if (type == 1) {
    if (get_refdb_pubyear(ptr_rendinfo->dbires, date_buffer) != NULL
	&& strcmp(date_buffer, "nd")) {
      nhave_year = 1;
    }
  }
  else {
    if (get_refdb_secyear(ptr_rendinfo->dbires, date_buffer) != NULL
	&& strcmp(date_buffer, "nd")) {
      nhave_year = 1;
    }
  }

/*   printf("date_buffer went to:%s<<\n", date_buffer); */
  if (nhave_year) {
/*     printf("nhave_year=%d || nhave_other+%d went to TRUE\n", nhave_year, nhave_other); */
    nhave_data++;
    if (print_element_x(date_buffer, &buffer1, &buffer1_len, "dateIssued", NULL, NULL, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
      free(buffer1);
      return NULL;
    }
  }

  if (nhave_data) {
    if ((new_ref = mstrcat(*ptr_buffer, buffer1, ptr_buffer_len, 0)) == NULL) {
      LOG_PRINT(LOG_WARNING, get_status_msg(801));
      free(buffer1);
      return NULL;
    }
    else {
      *ptr_buffer = new_ref;
    }
  }

  if (nhave_data) {
    (*ptr_nhave_buffer_data)++;
  }

  free(buffer1);
  return *ptr_buffer;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  add_reprint_mods() prints out mods reprint info

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

  char** ptr_buffer ptr to ptr to buffer that will receive the output

  size_t* ptr_buffer_len ptr to var holding size of *ptr_buffer

  dbi_conn conn connection to database

  struct renderinfo* ptr_renderinfo ptr to struct with render information

  char* username name of user 

  int* ptr_nhave_buffer_data ptr to var which will be set to non-zero
                             if *ptr_buffer receives data

  struct xmlindent* ptr_indent ptr to struct with indent info

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char* add_reprint_mods(char** ptr_buffer, size_t* ptr_buffer_len, dbi_conn conn, struct renderinfo* ptr_rendinfo, char* username, int* ptr_nhave_buffer_data, struct xmlindent* ptr_indent) {
  struct REPRINT reprint;
  char attribute[10];
  size_t path_skip = 0;

  if (get_reprint(ptr_rendinfo->dbires, &reprint, username, 4)
      && reprint.reprint != NULL && *(reprint.reprint)) {
    if (reprint.avail != NULL && *(reprint.avail)) {
      *ptr_nhave_buffer_data = 1;
      if (!strncmp(reprint.avail, "PATH:", 5)) {
	strcpy(attribute, "useroot");
	path_skip = 5;
      }
      else {
	strcpy(attribute, "full");
      }
      
      if (print_element_x(reprint.avail+path_skip, ptr_buffer, ptr_buffer_len, "physicalLocation", "type", attribute, NULL, NULL, ptr_indent, ptr_rendinfo->ptr_clrequest->namespace) == NULL) {
	return NULL;
      }
    }
  }
  return *ptr_buffer;
}

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

  static int indent_notbelow returns 1 if not to indent, 0 if to indent

  const char* name ptr to element name

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

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

  static int is_entry_mods returns 1 if entry starts, 0 if not

  const char* name ptr to element name

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