diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,8 @@
+# 6.8.0.0
+
+  - Import Bibutils 6.8
+
+
 # 6.7.0.0
 
   - Import Bibutils 6.7
diff --git a/bibutils/adsout.c b/bibutils/adsout.c
--- a/bibutils/adsout.c
+++ b/bibutils/adsout.c
@@ -1,8 +1,8 @@
 /*
  * adsout.c
  *
- * Copyright (c) Richard Mathar 2007-2018
- * Copyright (c) Chris Putnam 2007-2018
+ * Copyright (c) Richard Mathar 2007-2019
+ * Copyright (c) Chris Putnam 2007-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -16,39 +16,60 @@
 #include "str.h"
 #include "strsearch.h"
 #include "fields.h"
+#include "generic.h"
 #include "name.h"
 #include "title.h"
+#include "type.h"
 #include "url.h"
 #include "bibformats.h"
 
-static int  adsout_write( fields *in, FILE *fp, param *p, unsigned long refnum );
-static void adsout_writeheader( FILE *outptr, param *p );
+/*****************************************************
+ PUBLIC: int adsout_initparams()
+*****************************************************/
 
-void
-adsout_initparams( param *p, const char *progname )
+static int adsout_write( fields *in, FILE *fp, param *p, unsigned long refnum );
+static int adsout_assemble( fields *in, fields *out, param *pm, unsigned long refnum );
+
+int
+adsout_initparams( param *pm, const char *progname )
 {
-	p->writeformat      = BIBL_ADSABSOUT;
-	p->format_opts      = 0;
-	p->charsetout       = BIBL_CHARSET_DEFAULT;
-	p->charsetout_src   = BIBL_SRC_DEFAULT;
-	p->latexout         = 0;
-	p->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
-	p->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
-	p->xmlout           = BIBL_XMLOUT_FALSE;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->singlerefperfile = 0;
+	pm->writeformat      = BIBL_ADSABSOUT;
+	pm->format_opts      = 0;
+	pm->charsetout       = BIBL_CHARSET_DEFAULT;
+	pm->charsetout_src   = BIBL_SRC_DEFAULT;
+	pm->latexout         = 0;
+	pm->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
+	pm->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
+	pm->xmlout           = BIBL_XMLOUT_FALSE;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->singlerefperfile = 0;
 
-	if ( p->charsetout == BIBL_CHARSET_UNICODE ) {
-		p->utf8out = p->utf8bom = 1;
+	if ( pm->charsetout == BIBL_CHARSET_UNICODE ) {
+		pm->utf8out = pm->utf8bom = 1;
 	}
 
-	p->headerf = adsout_writeheader;
-	p->footerf = NULL;
-	p->writef  = adsout_write;
+	pm->headerf   = generic_writeheader;
+	pm->footerf   = NULL;
+	pm->assemblef = adsout_assemble;
+	pm->writef    = adsout_write;
+
+	if ( !pm->progname ) {
+		if ( !progname ) pm->progname=NULL;
+		else {
+			pm->progname = strdup( progname );
+			if ( !pm->progname ) return BIBL_ERR_MEMERR;
+		}
+	}
+
+	return BIBL_OK;
 }
 
+/*****************************************************
+ PUBLIC: int adsout_assemble()
+*****************************************************/
+
 enum {
 	TYPE_UNKNOWN = 0,
 	TYPE_GENERIC,
@@ -76,84 +97,57 @@
 	TYPE_PROGRAM
 };
 
-typedef struct match_type {
-	char *name;
-	int type;
-} match_type;
-
 static int
 get_type( fields *in )
 {
-	match_type match_genres[] = {
-		{ "academic journal",          TYPE_ARTICLE },
-		{ "magazine",                  TYPE_MAGARTICLE },
-		{ "conference publication",    TYPE_INPROCEEDINGS },
-		{ "hearing",                   TYPE_HEARING },
-		{ "Ph.D. thesis",              TYPE_PHDTHESIS },
-		{ "Masters thesis",            TYPE_MASTERSTHESIS },
-		{ "Diploma thesis",            TYPE_DIPLOMATHESIS },
-		{ "Doctoral thesis",           TYPE_DOCTORALTHESIS },
-		{ "Habilitation thesis",       TYPE_HABILITATIONTHESIS },
-		{ "Licentiate thesis",         TYPE_LICENTIATETHESIS },
-		{ "legislation",               TYPE_BILL },
-		{ "newspaper",                 TYPE_NEWSPAPER },
-		{ "communication",             TYPE_COMMUNICATION },
-		{ "manuscript",                TYPE_MANUSCRIPT },
-		{ "unpublished",               TYPE_MANUSCRIPT },
-		{ "report",                    TYPE_REPORT },
-		{ "technical report",          TYPE_REPORT },
-		{ "legal case and case notes", TYPE_CASE },
-		{ "patent",                    TYPE_PATENT },
+	match_type genre_matches[] = {
+		{ "academic journal",          TYPE_ARTICLE,            LEVEL_ANY },
+		{ "communication",             TYPE_COMMUNICATION,      LEVEL_ANY },
+		{ "conference publication",    TYPE_INPROCEEDINGS,      LEVEL_ANY },
+		{ "Diploma thesis",            TYPE_DIPLOMATHESIS,      LEVEL_ANY },
+		{ "Doctoral thesis",           TYPE_DOCTORALTHESIS,     LEVEL_ANY },
+		{ "Habilitation thesis",       TYPE_HABILITATIONTHESIS, LEVEL_ANY },
+		{ "hearing",                   TYPE_HEARING,            LEVEL_ANY },
+		{ "legal case and case notes", TYPE_CASE,               LEVEL_ANY },
+		{ "legislation",               TYPE_BILL,               LEVEL_ANY },
+		{ "Licentiate thesis",         TYPE_LICENTIATETHESIS,   LEVEL_ANY },
+		{ "magazine",                  TYPE_MAGARTICLE,         LEVEL_ANY },
+		{ "manuscript",                TYPE_MANUSCRIPT,         LEVEL_ANY },
+		{ "Masters thesis",            TYPE_MASTERSTHESIS,      LEVEL_ANY },
+		{ "newspaper",                 TYPE_NEWSPAPER,          LEVEL_ANY },
+		{ "patent",                    TYPE_PATENT,             LEVEL_ANY },
+		{ "Ph.D. thesis",              TYPE_PHDTHESIS,          LEVEL_ANY },
+		{ "report",                    TYPE_REPORT,             LEVEL_ANY },
+		{ "technical report",          TYPE_REPORT,             LEVEL_ANY },
+		{ "unpublished",               TYPE_MANUSCRIPT,         LEVEL_ANY },
+		{ "periodical",                TYPE_ARTICLE,            LEVEL_ANY  },
+		{ "thesis",                    TYPE_THESIS,             LEVEL_ANY  },
+		{ "book",                      TYPE_BOOK,               LEVEL_MAIN },
+		{ "book",                      TYPE_INBOOK,             LEVEL_ANY  },
+		{ "collection",                TYPE_BOOK,               LEVEL_MAIN },
+		{ "collection",                TYPE_INBOOK,             LEVEL_ANY  },
 	};
-	int nmatch_genres = sizeof( match_genres ) / sizeof( match_genres[0] );
+	int ngenre_matches = sizeof( genre_matches ) / sizeof( genre_matches[0] );
 
-	char *tag, *data;
-	int i, j, type = TYPE_UNKNOWN;
+	match_type resource_matches[] = {
+		{ "moving image",              TYPE_BROADCAST,          LEVEL_ANY  },
+		{ "software, multimedia",      TYPE_PROGRAM,            LEVEL_ANY  },
+	};
+	int nresource_matches = sizeof( resource_matches ) /sizeof( resource_matches[0] );
 
-	for ( i=0; i<in->n; ++i ) {
-		tag = in->tag[i].data;
-		if ( strcasecmp( tag, "GENRE:MARC" ) &&
-		     strcasecmp( tag, "GENRE:BIBUTILS" ) &&
-		     strcasecmp( tag, "GENRE:UNKNOWN" ) ) continue;
-		data = in->data[i].data;
-		for ( j=0; j<nmatch_genres; ++j ) {
-			if ( !strcasecmp( data, match_genres[j].name ) ) {
-				type = match_genres[j].type;
-				fields_setused( in, i );
-			}
-		}
-		if ( type==TYPE_UNKNOWN ) {
-			if ( !strcasecmp( data, "periodical" ) )
-				type = TYPE_ARTICLE;
-			else if ( !strcasecmp( data, "thesis" ) )
-				type = TYPE_THESIS;
-			else if ( !strcasecmp( data, "book" ) ) {
-				if ( in->level[i]==0 ) type = TYPE_BOOK;
-				else type = TYPE_INBOOK;
-			}
-			else if ( !strcasecmp( data, "collection" ) ) {
-				if ( in->level[i]==0 ) type = TYPE_BOOK;
-				else type = TYPE_INBOOK;
-			}
-			if ( type!=TYPE_UNKNOWN ) fields_setused( in, i );
-		}
-	}
-	if ( type==TYPE_UNKNOWN ) {
-		for ( i=0; i<in->n; ++i ) {
-			if ( strcasecmp( in->tag[i].data, "RESOURCE" ) )
-				continue;
-			data = in->data[i].data;
-			if ( !strcasecmp( data, "moving image" ) )
-				type = TYPE_BROADCAST;
-			else if ( !strcasecmp( data, "software, multimedia" ) )
-				type = TYPE_PROGRAM;
-			if ( type!=TYPE_UNKNOWN ) fields_setused( in, i );
-		}
-	}
+	match_type issuance_matches[] = {
+		{ "monographic",               TYPE_BOOK,               LEVEL_MAIN },
+		{ "monographic",               TYPE_INBOOK,             LEVEL_ANY  },
+	};
+	int nissuance_matches = sizeof( issuance_matches ) /sizeof( issuance_matches[0] );
 
-	/* default to generic */
+	int type;
+
+	type = type_from_mods_hints( in, TYPE_FROM_GENRE, genre_matches, ngenre_matches, TYPE_UNKNOWN );
+	if ( type==TYPE_UNKNOWN ) type = type_from_mods_hints( in, TYPE_FROM_RESOURCE, resource_matches, nresource_matches, TYPE_UNKNOWN );
+	if ( type==TYPE_UNKNOWN ) type = type_from_mods_hints( in, TYPE_FROM_ISSUANCE, issuance_matches, nissuance_matches, TYPE_UNKNOWN );
 	if ( type==TYPE_UNKNOWN ) type = TYPE_GENERIC;
-	
+
 	return type;
 }
 
@@ -318,7 +312,7 @@
 	str *month;
 
 	month = fields_findv_firstof( in, level, FIELDS_STRP, "DATE:MONTH", "PARTDATE:MONTH", NULL );
-	if ( str_has_value( month ) ) return mont2mont( month->data );
+	if ( str_has_value( month ) ) return mont2mont( str_cstr( month ) );
 	else return 0;
 }
 
@@ -633,24 +627,8 @@
 	}
 }
 
-static void
-output( FILE *fp, fields *out )
-{
-	char *tag, *value;
-	int i;
-
-	for ( i=0; i<out->n; ++i ) {
-		tag   = fields_tag( out, i, FIELDS_CHRP );
-		value = fields_value( out, i, FIELDS_CHRP );
-		fprintf( fp, "%s %s\n", tag, value );
-	}
-
-	fprintf( fp, "\n" );
-	fflush( fp );
-}
-
 static int
-append_data( fields *in, fields *out )
+adsout_assemble( fields *in, fields *out, param *pm, unsigned long refnum )
 {
 	int type, status = BIBL_OK;
 
@@ -678,25 +656,23 @@
 	return status;
 }
 
+/*****************************************************
+ PUBLIC: int adsout_write()
+*****************************************************/
+
 static int
-adsout_write( fields *in, FILE *fp, param *p, unsigned long refnum )
+adsout_write( fields *out, FILE *fp, param *p, unsigned long refnum )
 {
-	int status;
-	fields out;
-
-	fields_init( &out );
-
-	status = append_data( in, &out );
-	if ( status==BIBL_OK ) output( fp, &out );
-
-	fields_free( &out );
+	const char *tag, *value;
+	int i;
 
-	return status;
-}
+	for ( i=0; i<out->n; ++i ) {
+		tag   = fields_tag( out, i, FIELDS_CHRP );
+		value = fields_value( out, i, FIELDS_CHRP );
+		fprintf( fp, "%s %s\n", tag, value );
+	}
 
-static void
-adsout_writeheader( FILE *outptr, param *p )
-{
-	if ( p->utf8bom ) utf8_writebom( outptr );
+	fprintf( fp, "\n" );
+	fflush( fp );
+	return BIBL_OK;
 }
-
diff --git a/bibutils/bibcore.c b/bibutils/bibcore.c
--- a/bibutils/bibcore.c
+++ b/bibutils/bibcore.c
@@ -1,7 +1,7 @@
 /*
  * bibcore.c
  *
- * Copyright (c) Chris Putnam 2005-2018
+ * Copyright (c) Chris Putnam 2005-2019
  *
  * Source code released under the GPL version 2
  *
@@ -102,7 +102,7 @@
 	if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
 	status = slist_copy( &(np->corps), &(op->corps ) );
 	if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
-	
+
 	if ( !op->progname ) np->progname = NULL;
 	else {
 		np->progname = strdup( op->progname );
@@ -139,6 +139,7 @@
 	np->convertf = op->convertf;
 	np->headerf = op->headerf;
 	np->footerf = op->footerf;
+	np->assemblef = op->assemblef;
 	np->writef = op->writef;
 	np->all = op->all;
 	np->nall = op->nall;
@@ -233,14 +234,14 @@
 int
 bibl_addtoasis( param *p, char *d )
 {
-	str *s;
+	int status;
 
 	if ( !p ) return BIBL_ERR_BADINPUT;
 	if ( !d ) return BIBL_ERR_BADINPUT;
 
-	s = slist_addc( &(p->asis), d );
+	status = slist_addc( &(p->asis), d );
 
-	return ( s==NULL )? BIBL_ERR_MEMERR : BIBL_OK;
+	return ( status==SLIST_OK )? BIBL_OK : BIBL_ERR_MEMERR;
 }
 
 /* bibl_addtocorps()
@@ -250,14 +251,14 @@
 int
 bibl_addtocorps( param *p, char *d )
 {
-	str *s;
+	int status;
 
 	if ( !p ) return BIBL_ERR_BADINPUT;
 	if ( !d ) return BIBL_ERR_BADINPUT;
 
-	s = slist_addc( &(p->corps), d );
+	status = slist_addc( &(p->corps), d );
 
-	return ( s==NULL )? BIBL_ERR_MEMERR : BIBL_OK;
+	return ( status==SLIST_OK )? BIBL_OK : BIBL_ERR_MEMERR;
 }
 
 void
@@ -725,21 +726,22 @@
 static int
 get_citekeys( bibl *b, slist *citekeys )
 {
+	int i, n, status;
 	fields *f;
-	int i, n;
-	str *s;
+
 	for ( i=0; i<b->nrefs; ++i ) {
 		f = b->ref[i];
 		n = fields_find( f, "REFNUM", LEVEL_ANY );
 		if ( n==FIELDS_NOTFOUND ) n = generate_citekey( f, i );
 		if ( n!=FIELDS_NOTFOUND && f->data[n].data ) {
-			s = slist_add( citekeys, &(f->data[n]) );
-			if ( !s ) return BIBL_ERR_MEMERR;
+			status = slist_add( citekeys, &(f->data[n]) );
+			if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
 		} else {
-			s = slist_addc( citekeys, "" );
-			if ( !s ) return BIBL_ERR_MEMERR;
+			status = slist_addc( citekeys, "" );
+			if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
 		}
 	}
+
 	return BIBL_OK;
 }
 
@@ -961,17 +963,35 @@
 static int
 bibl_writeeachfp( FILE *fp, bibl *b, param *p )
 {
+	fields out, *use = &out;
 	int status;
 	long i;
+
+	fields_init( &out );
+
 	for ( i=0; i<b->nrefs; ++i ) {
+
 		fp = singlerefname( b->ref[i], i, p->writeformat );
 		if ( !fp ) return BIBL_ERR_CANTOPEN;
+
 		if ( p->headerf ) p->headerf( fp, p );
-		status = p->writef( b->ref[i], fp, p, i );
+
+		if ( p->assemblef ) {
+			fields_free( &out );
+			status = p->assemblef( b->ref[i], &out, p, i );
+			if ( status!=BIBL_OK ) break;
+		} else {
+			use = b->ref[i];
+		}
+
+		status = p->writef( use, fp, p, i );
+
 		if ( p->footerf ) p->footerf( fp );
 		fclose( fp );
+
 		if ( status!=BIBL_OK ) return status;
 	}
+
 	return BIBL_OK;
 }
 
@@ -979,12 +999,36 @@
 bibl_writefp( FILE *fp, bibl *b, param *p )
 {
 	int status = BIBL_OK;
+	fields out, *use = &out;
 	long i;
+
+	fields_init( &out );
+
+	if ( debug_set( p ) && p->assemblef ) {
+		fprintf( stderr, "-------------------assemblef start for bibl_write\n");
+	}
+
 	if ( p->headerf ) p->headerf( fp, p );
 	for ( i=0; i<b->nrefs; ++i ) {
-		status = p->writef( b->ref[i], fp, p, i );
+
+		if ( p->assemblef ) {
+			fields_free( &out );
+			status = p->assemblef( b->ref[i], &out, p, i );
+			if ( status!=BIBL_OK ) break;
+			if ( debug_set( p ) ) bibl_verbose2( &out, "", i+1 );
+		} else {
+			use = b->ref[i];
+		}
+
+		status = p->writef( use, fp, p, i );
 		if ( status!=BIBL_OK ) break;
+
 	}
+
+	if ( debug_set( p ) && p->assemblef ) {
+		fprintf( stderr, "-------------------assemblef end for bibl_write\n");
+	}
+
 	if ( p->footerf ) p->footerf( fp );
 	return status;
 }
@@ -1003,23 +1047,32 @@
 	status = bibl_setwriteparams( &lp, p );
 	if ( status!=BIBL_OK ) return status;
 
-	status = bibl_fixcharsets( b, &lp );
-	if ( status!=BIBL_OK ) return status;
-
 	if ( debug_set( p ) ) {
 		report_params( stderr, "bibl_write", &lp );
 		fflush( stdout );
-		fprintf( stderr, "-------------------start for bibl_write\n");
+	}
+
+	if ( debug_set( p ) ) {
+		fprintf( stderr, "-------------------raw input start for bibl_write\n");
 		bibl_verbose0( b );
-		fprintf( stderr, "-------------------end for bibl_write\n" );
+		fprintf( stderr, "-------------------raw input end for bibl_write\n" );
 		fflush( stderr );
 	}
 
+	status = bibl_fixcharsets( b, &lp );
+	if ( status!=BIBL_OK ) goto out;
+
+	if ( debug_set( p ) ) {
+		fprintf( stderr, "-------------------post-fixcharsets start for bibl_write\n");
+		bibl_verbose0( b );
+		fprintf( stderr, "-------------------post-fixcharsets end for bibl_write\n" );
+		fflush( stderr );
+	}
+
 	if ( p->singlerefperfile ) status = bibl_writeeachfp( fp, b, &lp );
 	else status = bibl_writefp( fp, b, &lp );
 
+out:
 	bibl_freeparams( &lp );
-
 	return status;
 }
-
diff --git a/bibutils/bibformats.h b/bibutils/bibformats.h
--- a/bibutils/bibformats.h
+++ b/bibutils/bibformats.h
@@ -1,7 +1,7 @@
 /*
  * bibformats.h
  *
- * Copyright (c) Chris Putnam 2007-2018
+ * Copyright (c) Chris Putnam 2007-2019
  *
  * Source code released under the GPL version 2
  *
@@ -11,26 +11,25 @@
 
 #include "bibutils.h"
 
-void adsout_initparams    ( param *p, const char *progname );
-void biblatexin_initparams( param *p, const char *progname );
-void bibtexin_initparams  ( param *p, const char *progname );
-void bibtexout_initparams ( param *p, const char *progname );
-void copacin_initparams   ( param *p, const char *progname );
-void ebiin_initparams     ( param *p, const char *progname );
-void endin_initparams     ( param *p, const char *progname );
-void endout_initparams    ( param *p, const char *progname );
-void endxmlin_initparams  ( param *p, const char *progname );
-void isiin_initparams     ( param *p, const char *progname );
-void isiout_initparams    ( param *p, const char *progname );
-void medin_initparams     ( param *p, const char *progname );
-void modsin_initparams    ( param *p, const char *progname );
-void modsout_initparams   ( param *p, const char *progname );
-void nbibin_initparams    ( param *p, const char *progname );
-void nbibout_initparams   ( param *p, const char *progname );
-void risin_initparams     ( param *p, const char *progname );
-void risout_initparams    ( param *p, const char *progname );
-void wordin_initparams    ( param *p, const char *progname );
-void wordout_initparams   ( param *p, const char *progname );
+int adsout_initparams    ( param *pm, const char *progname );
+int biblatexin_initparams( param *pm, const char *progname );
+int bibtexin_initparams  ( param *pm, const char *progname );
+int bibtexout_initparams ( param *pm, const char *progname );
+int copacin_initparams   ( param *pm, const char *progname );
+int ebiin_initparams     ( param *pm, const char *progname );
+int endin_initparams     ( param *pm, const char *progname );
+int endout_initparams    ( param *pm, const char *progname );
+int endxmlin_initparams  ( param *pm, const char *progname );
+int isiin_initparams     ( param *pm, const char *progname );
+int isiout_initparams    ( param *pm, const char *progname );
+int medin_initparams     ( param *pm, const char *progname );
+int modsin_initparams    ( param *pm, const char *progname );
+int modsout_initparams   ( param *pm, const char *progname );
+int nbibin_initparams    ( param *pm, const char *progname );
+int nbibout_initparams   ( param *pm, const char *progname );
+int risin_initparams     ( param *pm, const char *progname );
+int risout_initparams    ( param *pm, const char *progname );
+int wordin_initparams    ( param *pm, const char *progname );
+int wordout_initparams   ( param *pm, const char *progname );
 
 #endif
-
diff --git a/bibutils/bibl.c b/bibutils/bibl.c
--- a/bibutils/bibl.c
+++ b/bibutils/bibl.c
@@ -1,7 +1,7 @@
 /*
  * bibl.c
  *
- * Copyright (c) Chris Putnam 2005-2018
+ * Copyright (c) Chris Putnam 2005-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/bibl.h b/bibutils/bibl.h
--- a/bibutils/bibl.h
+++ b/bibutils/bibl.h
@@ -1,7 +1,7 @@
 /*
  * bibl.h
  *
- * Copyright (c) Chris Putnam 2005-2018
+ * Copyright (c) Chris Putnam 2005-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/biblatexin.c b/bibutils/biblatexin.c
--- a/bibutils/biblatexin.c
+++ b/bibutils/biblatexin.c
@@ -1,8 +1,8 @@
 /*
  * biblatexin.c
  *
- * Copyright (c) Chris Putnam 2008-2018
- * Copyright (c) Johannes Wilm 2010-2018
+ * Copyright (c) Chris Putnam 2008-2019
+ * Copyright (c) Johannes Wilm 2010-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -34,38 +34,43 @@
 *****************************************************/
 
 static int  biblatexin_convertf( fields *bibin, fields *info, int reftype, param *p );
-static int  biblatexin_processf( fields *bibin, char *data, char *filename, long nref, param *p );
+static int  biblatexin_processf( fields *bibin, const char *data, const char *filename, long nref, param *p );
 static int  biblatexin_cleanf( bibl *bin, param *p );
 static int  biblatexin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int  biblatexin_typef( fields *bibin, char *filename, int nrefs, param *p );
+static int  biblatexin_typef( fields *bibin, const char *filename, int nrefs, param *p );
 
-void
-biblatexin_initparams( param *p, const char *progname )
+int
+biblatexin_initparams( param *pm, const char *progname )
 {
-	p->readformat       = BIBL_BIBLATEXIN;
-	p->charsetin        = BIBL_CHARSET_DEFAULT;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 1;
-	p->xmlin            = 0;
-	p->utf8in           = 0;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->output_raw       = 0;
+	pm->readformat       = BIBL_BIBLATEXIN;
+	pm->charsetin        = BIBL_CHARSET_DEFAULT;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 1;
+	pm->xmlin            = 0;
+	pm->utf8in           = 0;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->output_raw       = 0;
 
-	p->readf    = biblatexin_readf;
-	p->processf = biblatexin_processf;
-	p->cleanf   = biblatexin_cleanf;
-	p->typef    = biblatexin_typef;
-	p->convertf = biblatexin_convertf;
-	p->all      = biblatex_all;
-	p->nall     = biblatex_nall;
+	pm->readf    = biblatexin_readf;
+	pm->processf = biblatexin_processf;
+	pm->cleanf   = biblatexin_cleanf;
+	pm->typef    = biblatexin_typef;
+	pm->convertf = biblatexin_convertf;
+	pm->all      = biblatex_all;
+	pm->nall     = biblatex_nall;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( !pm->progname ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 /*****************************************************
@@ -97,7 +102,7 @@
 biblatexin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
 	int haveref = 0;
-	char *p;
+	const char *p;
 	while ( haveref!=2 && readmore( fp, buf, bufsize, bufpos, line ) ) {
 		if ( line->len == 0 ) continue; /* blank line */
 		p = &(line->data[0]);
@@ -112,7 +117,6 @@
 			str_addchar( reference, '\n' );
 			str_empty( line );
 		} else if ( !haveref ) str_empty( line );
-	
 	}
 	*fcharset = CHARSET_UNKNOWN;
 	return haveref;
@@ -122,8 +126,8 @@
  PUBLIC: int biblatexin_processf()
 *****************************************************/
 
-static char *
-process_biblatextype( char *p, str *type )
+static const char *
+process_biblatextype( const char *p, str *type )
 {
 	str tmp;
 	str_init( &tmp );
@@ -141,10 +145,10 @@
 	return p;
 }
 
-static char *
-process_biblatexid( char *p, str *id )
+static const char *
+process_biblatexid( const char *p, str *id )
 {
-	char *start_p = p;
+	const char *start_p = p;
 	str tmp;
 
 	str_init( &tmp );
@@ -170,19 +174,20 @@
 	return skip_ws( p );
 }
 
-static char *
-biblatex_tag( char *p, str *tag )
+static const char *
+biblatex_tag( const char *p, str *tag )
 {
 	p = str_cpytodelim( tag, skip_ws( p ), "= \t\r\n", 0 );
 	return skip_ws( p );
 }
 
-static char *
-biblatex_data( char *p, fields *bibin, slist *tokens, long nref, param *pm )
+static const char *
+biblatex_data( const char *p, fields *bibin, slist *tokens, long nref, param *pm )
 {
 	unsigned int nbracket = 0, nquotes = 0;
-	char *startp = p;
-	str tok, *s;
+	const char *startp = p;
+	int status;
+	str tok;
 
 	str_init( &tok );
 	while ( p && *p ) {
@@ -194,18 +199,18 @@
 			nquotes = !nquotes;
 			str_addchar( &tok, *p );
 			if ( !nquotes ) {
-				s = slist_add( tokens, &tok );
-				if ( !s ) { p = NULL; goto outerr; }
+				status = slist_add( tokens, &tok );
+				if ( status!=SLIST_OK ) { p = NULL; goto outerr; }
 				str_empty( &tok );
 			}
 		} else if ( *p=='#' && !nquotes && !nbracket ) {
 			if ( str_has_value( &tok ) ) {
-				s = slist_add( tokens, &tok );
-				if ( !s ) { p = NULL; goto outerr; }
+				status = slist_add( tokens, &tok );
+				if ( status!=SLIST_OK ) { p = NULL; goto outerr; }
 			}
 			str_strcpyc( &tok, "#" );
-			s = slist_add( tokens, &tok );
-			if ( !s ) { p = NULL; goto outerr; }
+			status = slist_add( tokens, &tok );
+			if ( status!=SLIST_OK ) { p = NULL; goto outerr; }
 			str_empty( &tok );
 		} else if ( *p=='{' && !nquotes && ( p==startp || *(p-1)!='\\' ) ) {
 			nbracket++;
@@ -214,8 +219,8 @@
 			nbracket--;
 			str_addchar( &tok, *p );
 			if ( nbracket==0 ) {
-				s = slist_add( tokens, &tok );
-				if ( !s ) { p = NULL; goto outerr; }
+				status = slist_add( tokens, &tok );
+				if ( status!=SLIST_OK ) { p = NULL; goto outerr; }
 				str_empty( &tok );
 			}
 		} else if ( !is_ws( *p ) || nquotes || nbracket ) {
@@ -230,8 +235,8 @@
 			}
 		} else if ( is_ws( *p ) ) {
 			if ( str_has_value( &tok ) ) {
-				s = slist_add( tokens, &tok );
-				if ( !s ) { p = NULL; goto outerr; }
+				status = slist_add( tokens, &tok );
+				if ( status!=SLIST_OK ) { p = NULL; goto outerr; }
 				str_empty( &tok );
 			}
 		}
@@ -245,8 +250,8 @@
 		fprintf( stderr, "%s: Mismatch in number of quotes in reference %ld\n", pm->progname, nref );
 	}
 	if ( str_has_value( &tok ) ) {
-		s = slist_add( tokens, &tok );
-		if ( !s ) p = NULL;
+		status = slist_add( tokens, &tok );
+		if ( status!=SLIST_OK ) p = NULL;
 	}
 outerr:
 	str_free( &tok );
@@ -310,7 +315,7 @@
 				fprintf( stderr, "%s: Warning: String concentation should "
 					"be used in context of quotations marks in reference %ld\n", pm->progname, nref );
 			t = slist_str( tokens, i+1 );
-			if ( t->data[0]!='\"' && t->data[s->len-1]!='\"' )
+			if ( t->data[0]!='\"' && t->data[t->len-1]!='\"' )
 				fprintf( stderr, "%s: Warning: String concentation should "
 					"be used in context of quotations marks in reference %ld\n", pm->progname, nref );
 			if ( ( s->data[s->len-1]=='\"' && t->data[0]=='\"') || (s->data[s->len-1]=='}' && t->data[0]=='{') ) {
@@ -329,8 +334,8 @@
 	return BIBL_OK;
 }
 
-static char *
-process_biblatexline( char *p, str *tag, str *data, uchar stripquotes, long nref, param *pm )
+static const char *
+process_biblatexline( const char *p, str *tag, str *data, uchar stripquotes, long nref, param *pm )
 {
 	int i, status;
 	slist tokens;
@@ -373,7 +378,7 @@
 }
 
 static int
-process_cite( fields *bibin, char *p, char *filename, long nref, param *pm )
+process_cite( fields *bibin, const char *p, const char *filename, long nref, param *pm )
 {
 	int fstatus, status = BIBL_OK;
 	str type, id, tag, data;
@@ -418,7 +423,7 @@
  *
  */
 static int
-process_string( char *p, long nref, param *pm )
+process_string( const char *p, long nref, param *pm )
 {
 	int n, status = BIBL_OK;
 	str s1, s2, *s;
@@ -429,15 +434,16 @@
 	if ( str_has_value( &s2 ) ) {
 		str_findreplace( &s2, "\\ ", " " );
 		if ( str_memerr( &s2 ) ) { status = BIBL_ERR_MEMERR; goto out; }
+	} else {
+		str_strcpyc( &s2, "" );
 	}
 	if ( str_has_value( &s1 ) ) {
 		n = slist_find( &find, &s1 );
 		if ( n==-1 ) {
-			s = slist_add( &find, &s1 );
-			if ( s==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
-			if ( str_has_value( &s2 ) ) s = slist_add( &replace, &s2 );
-			else s = slist_addc( &replace, "" );
-			if ( s==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
+			status = slist_add_ret( &find, &s1, BIBL_OK, BIBL_ERR_MEMERR );
+			if ( status!=BIBL_OK ) goto out;
+			status = slist_add_ret( &replace, &s2, BIBL_OK, BIBL_ERR_MEMERR );
+			if ( status!=BIBL_OK ) goto out;
 		} else {
 			if ( str_has_value( &s2 ) ) s = slist_set( &replace, n, &s2 );
 			else s = slist_setc( &replace, n, "" );
@@ -450,7 +456,7 @@
 }
 
 static int
-biblatexin_processf( fields *bibin, char *data, char *filename, long nref, param *p )
+biblatexin_processf( fields *bibin, const char *data, const char *filename, long nref, param *p )
 {
 	if ( !strncasecmp( data, "@STRING", 7 ) ) {
 		process_string( data+7, nref, p );
@@ -570,16 +576,16 @@
 		} else if ( is_ws( s->data[i] ) ) {
 			if ( str_memerr( &tok ) ) { status = BIBL_ERR_MEMERR; goto out; }
 			if ( str_has_value( &tok ) ) {
-				t = slist_add( tokens, &tok );
-				if ( !t ) { status = BIBL_ERR_MEMERR; goto out; }
+				status = slist_add_ret( tokens, &tok, BIBL_OK, BIBL_ERR_MEMERR );
+				if ( status!=BIBL_OK ) goto out;
 			}
 			str_empty( &tok );
 		}
 	}
 	if ( str_has_value( &tok ) ) {
 		if ( str_memerr( &tok ) ) { status = BIBL_ERR_MEMERR; goto out; }
-		t = slist_add( tokens, &tok );
-		if ( !t ) { status = BIBL_ERR_MEMERR; goto out; }
+		status = slist_add_ret( tokens, &tok, BIBL_OK, BIBL_ERR_MEMERR );
+		if ( status!=BIBL_OK ) goto out;
 	}
 
 	for ( i=0; i<tokens->n; ++i ) {
@@ -597,7 +603,7 @@
 biblatexin_addtitleurl( fields *info, str *in )
 {
 	int fstatus, status = BIBL_OK;
-	char *p;
+	const char *p;
 	str s;
 
 	str_init( &s );
@@ -802,7 +808,7 @@
 *****************************************************/
 
 static int
-biblatexin_typef( fields *bibin, char *filename, int nrefs, param *p )
+biblatexin_typef( fields *bibin, const char *filename, int nrefs, param *p )
 {
 	int ntypename, nrefname, is_default;
 	char *refname = "", *typename="";
diff --git a/bibutils/bibtexin.c b/bibutils/bibtexin.c
--- a/bibutils/bibtexin.c
+++ b/bibutils/bibtexin.c
@@ -1,7 +1,7 @@
 /*
  * bibtexin.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -33,39 +33,44 @@
  PUBLIC: void bibtexin_initparams()
 *****************************************************/
 
-static int  bibtexin_convertf( fields *bibin, fields *info, int reftype, param *p );
-static int  bibtexin_processf( fields *bibin, char *data, char *filename, long nref, param *p );
-static int  bibtexin_cleanf( bibl *bin, param *p );
-static int  bibtexin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int  bibtexin_typef( fields *bibin, char *filename, int nrefs, param *p );
+static int bibtexin_convertf( fields *bibin, fields *info, int reftype, param *p );
+static int bibtexin_processf( fields *bibin, const char *data, const char *filename, long nref, param *p );
+static int bibtexin_cleanf( bibl *bin, param *p );
+static int bibtexin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
+static int bibtexin_typef( fields *bibin, const char *filename, int nrefs, param *p );
 
-void
-bibtexin_initparams( param *p, const char *progname )
+int
+bibtexin_initparams( param *pm, const char *progname )
 {
-	p->readformat       = BIBL_BIBTEXIN;
-	p->charsetin        = BIBL_CHARSET_DEFAULT;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 1;
-	p->xmlin            = 0;
-	p->utf8in           = 0;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->output_raw       = 0;
+	pm->readformat       = BIBL_BIBTEXIN;
+	pm->charsetin        = BIBL_CHARSET_DEFAULT;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 1;
+	pm->xmlin            = 0;
+	pm->utf8in           = 0;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->output_raw       = 0;
 
-	p->readf    = bibtexin_readf;
-	p->processf = bibtexin_processf;
-	p->cleanf   = bibtexin_cleanf;
-	p->typef    = bibtexin_typef;
-	p->convertf = bibtexin_convertf;
-	p->all      = bibtex_all;
-	p->nall     = bibtex_nall;
+	pm->readf    = bibtexin_readf;
+	pm->processf = bibtexin_processf;
+	pm->cleanf   = bibtexin_cleanf;
+	pm->typef    = bibtexin_typef;
+	pm->convertf = bibtexin_convertf;
+	pm->all      = bibtex_all;
+	pm->nall     = bibtex_nall;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( pm->progname==NULL ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 /*****************************************************
@@ -97,7 +102,7 @@
 bibtexin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
 	int haveref = 0;
-	char *p;
+	const char *p;
 	*fcharset = CHARSET_UNKNOWN;
 	while ( haveref!=2 && readmore( fp, buf, bufsize, bufpos, line ) ) {
 		if ( line->len == 0 ) continue; /* blank line */
@@ -130,15 +135,34 @@
  PUBLIC: int bibtexin_processf()
 *****************************************************/
 
-static char*
-process_bibtextype( char *p, str *type )
+typedef struct loc {
+	const char *progname;
+	const char *filename;
+	long nref;
+} loc;
+
+/* process_bibtextype()
+ *
+ * extract 'article', 'book', etc. from:
+ *
+ * @article{...}
+ * @book(...)
+ *
+ * return pointer after '{' or '(' character
+ */
+static const char*
+process_bibtextype( const char *p, str *type )
 {
 	str tmp;
+
 	str_init( &tmp );
 
 	if ( *p=='@' ) p++;
+	p = skip_ws( p );
+
 	p = str_cpytodelim( &tmp, p, "{( \t\r\n", 0 );
 	p = skip_ws( p );
+
 	if ( *p=='{' || *p=='(' ) p++;
 	p = skip_ws( p );
 
@@ -146,13 +170,14 @@
 	else str_empty( type );
 
 	str_free( &tmp );
+
 	return p;
 }
 
-static char*
-process_bibtexid( char *p, str *id )
+static const char *
+process_bibtexid( const char *p, str *id )
 {
-	char *start_p = p;
+	const char *start_p = p;
 	str tmp;
 
 	str_init( &tmp );
@@ -178,219 +203,342 @@
 	return skip_ws( p );
 }
 
-static char *
-bibtex_tag( char *p, str *tag )
+/* bibtex_tag()
+ *
+ * returns NULL on memory error, else position after tag+whitespace
+ */
+static const char *
+bibtex_tag( const char *p, str *tag )
 {
-	p = str_cpytodelim( tag, skip_ws( p ), "= \t\r\n", 0 );
+	p = str_cpytodelim( tag, p, "= \t\r\n", 0 );
 	if ( str_memerr( tag ) ) return NULL;
 	return skip_ws( p );
 }
 
-static char *
-bibtex_data( char *p, fields *bibin, slist *tokens, long nref, param *pm )
+static int
+quotation_mark_is_escaped( int nbraces, const char *p, const char *startp )
 {
-	unsigned int nbracket = 0, nquotes = 0;
-	char *startp = p;
-	str tok, *t;
+	if ( nbraces!=0 ) return 1;
+	if ( p!=startp && *(p-1)=='\\' ) return 1;
+	return 0;
+}
 
-	str_init( &tok );
+static int
+brace_is_escaped( int nquotes, const char *p, const char *startp )
+{
+	if ( nquotes!=0 ) return 1;
+	if ( p!=startp && *(p-1)=='\\' ) return 1;
+	return 0;
+}
+
+static int
+char_is_escaped( int nquotes, int nbraces )
+{
+	if ( nquotes!=0 || nbraces!=0 ) return 1;
+	return 0;
+}
+
+static int
+add_token( slist *tokens, str *token )
+{
+	int status;
+
+	if ( str_memerr( token ) ) return BIBL_ERR_MEMERR;
+
+	status = slist_add( tokens, token );
+	if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
+
+	str_empty( token );
+
+	return BIBL_OK;
+}
+
+static const char *
+bibtex_data( const char *p, slist *tokens, loc *currloc )
+{
+	int nbraces = 0, nquotes = 0;
+	const char *startp = p;
+	int status;
+	str token;
+
+	str_init( &token );
+
 	while ( p && *p ) {
-		if ( !nquotes && !nbracket ) {
-			if ( *p==',' || *p=='=' || *p=='}' || *p==')' )
-				goto out;
+
+		/* ...have we reached end-of-data? */
+		if ( nquotes==0 && nbraces==0 ) {
+			if ( *p==',' || *p=='=' || *p=='}' || *p==')' ) goto out;
 		}
-		if ( *p=='\"' && nbracket==0 && ( p==startp || *(p-1)!='\\' ) ) {
-			nquotes = !nquotes;
-			str_addchar( &tok, *p );
-			if ( !nquotes ) {
-				if ( str_memerr( &tok ) ) { p=NULL; goto out; }
-				t = slist_add( tokens, &tok );
-				if ( !t ) { p=NULL; goto out0; }
-				str_empty( &tok );
+
+		if ( *p=='\"' ) {
+			str_addchar( &token, *p );
+			if ( !quotation_mark_is_escaped( nbraces, p, startp ) ) {
+				nquotes = !nquotes;
+				if ( nquotes==0 ) {
+					status = add_token( tokens, &token );
+					if ( status!=BIBL_OK ) { p=NULL; goto out0; }
+				}
 			}
-		} else if ( *p=='#' && !nquotes && !nbracket ) {
-			if ( str_has_value( &tok ) ) {
-				if ( str_memerr( &tok ) ) { p=NULL; goto out; }
-				t = slist_add( tokens, &tok );
-				if ( !t ) { p=NULL; goto out0; }
+		}
+
+		else if ( *p=='{' ) {
+			str_addchar( &token, *p );
+			if ( !brace_is_escaped( nquotes, p, startp ) ) {
+				nbraces++;
 			}
-			str_strcpyc( &tok, "#" );
-			t = slist_add( tokens, &tok );
-			if ( !t ) { p=NULL; goto out0; }
-			str_empty( &tok );
-		} else if ( *p=='{' && !nquotes && ( p==startp || *(p-1)!='\\' ) ) {
-			nbracket++;
-			str_addchar( &tok, *p );
-		} else if ( *p=='}' && !nquotes && ( p==startp || *(p-1)!='\\' ) ) {
-			nbracket--;
-			str_addchar( &tok, *p );
-			if ( nbracket==0 ) {
-				if ( str_memerr( &tok ) ) { p=NULL; goto out; }
-				t = slist_add( tokens, &tok );
-				if ( !t ) { p=NULL; goto out; }
-				str_empty( &tok );
+		}
+
+		else if ( *p=='}' ) {
+			str_addchar( &token, *p );
+			if ( !brace_is_escaped( nquotes, p, startp ) ) {
+				nbraces--;
+				if ( nbraces==0 ) {
+					status = add_token( tokens, &token );
+					if ( status!=BIBL_OK ) { p=NULL; goto out0; }
+				}
+				if ( nbraces<0 ) {
+					goto out;
+				}
 			}
-		} else if ( !is_ws( *p ) || nquotes || nbracket ) {
-			if ( !is_ws( *p ) ) str_addchar( &tok, *p );
+		}
+
+		else if ( *p=='#' ) {
+			if ( char_is_escaped( nquotes, nbraces ) ) {
+				str_addchar( &token, *p );
+			}
+			/* ...this is a bibtex string concatentation token */
 			else {
-				if ( tok.len!=0 && *p!='\n' && *p!='\r' )
-					str_addchar( &tok, *p );
-				else if ( tok.len!=0 && (*p=='\n' || *p=='\r')) {
-					str_addchar( &tok, ' ' );
+				if ( str_has_value( &token ) ) {
+					status = add_token( tokens, &token );
+					if ( status!=BIBL_OK ) { p=NULL; goto out0; }
+				}
+				status = slist_addc( tokens, "#" );
+				if ( status!=SLIST_OK ) { p=NULL; goto out0; }
+			}
+		}
+
+		/* ...add escaped white-space and non-white-space to current token */
+		else if ( !is_ws( *p ) || char_is_escaped( nquotes, nbraces ) ) {
+			/* always add non-whitespace characters */
+			if ( !is_ws( *p ) ) {
+				str_addchar( &token, *p );
+			}
+			/* only add whitespace if token is non-empty; convert CR/LF to space */
+			else if ( token.len!=0 ) {
+				if ( *p!='\n' && *p!='\r' )
+					str_addchar( &token, *p );
+				else {
+					str_addchar( &token, ' ' );
 					while ( is_ws( *(p+1) ) ) p++;
 				}
 			}
-		} else if ( is_ws( *p ) ) {
-			if ( tok.len ) {
-				if ( str_memerr( &tok ) ) { p=NULL; goto out; }
-				t = slist_add( tokens, &tok );
-				if ( !t ) { p=NULL; goto out; }
-				str_empty( &tok );
+		}
+
+		/* ...unescaped white-space marks the end of a token */
+		else if ( is_ws( *p ) ) {
+			if ( token.len ) {
+				status = add_token( tokens, &token );
+				if ( status!=BIBL_OK ) { p=NULL; goto out0; }
 			}
 		}
+
 		p++;
 	}
 out:
-	if ( nbracket!=0 ) {
-		fprintf( stderr, "%s: Mismatch in number of brackets in reference %ld.\n", pm->progname, nref );
+	if ( nbraces!=0 ) {
+		fprintf( stderr, "%s: Mismatch in number of braces in file %s reference %ld.\n", currloc->progname, currloc->filename, currloc->nref );
 	}
 	if ( nquotes!=0 ) {
-		fprintf( stderr, "%s: Mismatch in number of quotes in reference %ld.\n", pm->progname, nref );
+		fprintf( stderr, "%s: Mismatch in number of quotes in file %s reference %ld.\n", currloc->progname, currloc->filename, currloc->nref );
 	}
-	if ( str_has_value( &tok ) ) {
-		if ( str_memerr( &tok ) ) { p = NULL; goto out; }
-		t = slist_add( tokens, &tok );
-		if ( !t ) p = NULL;
+	if ( str_has_value( &token ) ) {
+		if ( str_memerr( &token ) ) { p = NULL; goto out; }
+		status = slist_add( tokens, &token );
+		if ( status!=SLIST_OK ) p = NULL;
 	}
 out0:
-	str_free( &tok );
+	str_free( &token );
 	return p;
 }
 
+#define NOT_ESCAPED    (0)
+#define ESCAPED_QUOTES (1)
+#define ESCAPED_BRACES (2)
+
+static int
+token_is_escaped( str *s )
+{
+	if ( s->data[0]=='\"' && s->data[s->len-1]=='\"' ) return ESCAPED_QUOTES;
+	if ( s->data[0]=='{'  && s->data[s->len-1]=='}'  ) return ESCAPED_BRACES;
+	return NOT_ESCAPED;
+}
+
 /* replace_strings()
  *
- * do string replacement -- only if unprotected by quotation marks or curly brackets
+ * do bibtex string replacement for data tokens
  */
-static void
-replace_strings( slist *tokens, fields *bibin, param *pm )
+static int
+replace_strings( slist *tokens )
 {
-	int i, n, ok;
-	char *q;
+	int i, n;
 	str *s;
-	i = 0;
-	while ( i < tokens->n ) {
+
+	for ( i=0; i<tokens->n; ++i ) {
+
 		s = slist_str( tokens, i );
-		if ( !strcmp( s->data, "#" ) ) {
-		} else if ( s->data[0]!='\"' && s->data[0]!='{' ) {
-			n = slist_find( &find, s );
-			if ( n!=-1 ) {
-				str_strcpy( s, slist_str( &replace, n ) );
-			} else {
-				q = s->data;
-				ok = 1;
-				while ( *q && ok ) {
-					if ( !isdigit( *q ) ) ok = 0;
-					q++;
-				}
-			}
-		}
-		i++;
+
+		/* ...skip if token is protected by quotation marks or braces */
+		if ( token_is_escaped( s ) ) continue;
+
+		/* ...skip if token is string concatentation symbol */
+		if ( !str_strcmpc( s, "#" ) ) continue;
+
+		n = slist_find( &find, s );
+		if ( slist_wasnotfound( &find, n ) ) continue;
+
+		str_strcpy( s, slist_str( &replace, n ) );
+		if ( str_memerr( s ) ) return BIBL_ERR_MEMERR;
+
 	}
+
+	return BIBL_OK;
 }
 
 static int
-string_concatenate( slist *tokens, fields *bibin, long nref, param *pm )
+string_concatenate( slist *tokens, loc *currloc )
 {
-	int i, status;
+	int i, status, esc_s, esc_t;
 	str *s, *t;
+
 	i = 0;
 	while ( i < tokens->n ) {
+
 		s = slist_str( tokens, i );
-		if ( !strcmp( s->data, "#" ) ) {
-			if ( i==0 || i==tokens->n-1 ) {
-				fprintf( stderr, "%s: Warning: Stray string concatenation "
-					"('#' character) in reference %ld\n", pm->progname, nref );
-				status = slist_remove( tokens, i );
-				if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
-				continue;
-			}
-			s = slist_str( tokens, i-1 );
-			if ( s->data[0]!='\"' && s->data[s->len-1]!='\"' )
-				fprintf( stderr, "%s: Warning: String concentation should "
-					"be used in context of quotations marks in reference %ld\n", pm->progname, nref );
-			t = slist_str( tokens, i+1 );
-			if ( t->data[0]!='\"' && t->data[s->len-1]!='\"' )
-				fprintf( stderr, "%s: Warning: String concentation should "
-					"be used in context of quotations marks in reference %ld\n", pm->progname, nref );
-			if ( ( s->data[s->len-1]=='\"' && t->data[0]=='\"') || (s->data[s->len-1]=='}' && t->data[0]=='{') ) {
-				str_trimend( s, 1 );
-				str_trimbegin( t, 1 );
-				str_strcat( s, t );
-			} else {
-				str_strcat( s, t );
-			}
-			status = slist_remove( tokens, i );
-			if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
+		if ( str_strcmpc( s, "#" ) ) {
+			i++;
+			continue;
+		}
+
+		if ( i==0 || i==tokens->n-1 ) {
+			fprintf( stderr, "%s: Warning: Stray string concatenation ('#' character) in file %s reference %ld\n",
+					currloc->progname, currloc->filename, currloc->nref );
 			status = slist_remove( tokens, i );
 			if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
-		} else i++;
+			continue;
+		}
+
+		s = slist_str( tokens, i-1 );
+		t = slist_str( tokens, i+1 );
+
+		esc_s = token_is_escaped( s );
+		esc_t = token_is_escaped( t );
+
+		if ( esc_s != NOT_ESCAPED ) str_trimend( s, 1 );
+		if ( esc_t != NOT_ESCAPED ) str_trimbegin( t, 1 );
+		if ( esc_s != esc_t ) {
+			if ( esc_s == NOT_ESCAPED ) {
+				if ( esc_t == ESCAPED_QUOTES ) str_prepend( s, "\"" );
+				else                           str_prepend( s, "{" );
+			}
+			else {
+				if ( esc_t != NOT_ESCAPED ) str_trimend( t, 1 );
+				if ( esc_s == ESCAPED_QUOTES ) str_addchar( t, '\"' );
+				else                           str_addchar( t, '}' );
+			}
+		}
+
+		str_strcat( s, t );
+		if ( str_memerr( s ) ) return BIBL_ERR_MEMERR;
+
+		/* ...remove concatenated string t */
+		status = slist_remove( tokens, i+1 );
+		if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
+
+		/* ...remove concatentation token '#' */
+		status = slist_remove( tokens, i );
+		if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
+
 	}
+
 	return BIBL_OK;
 }
 
-/* return NULL on memory error */
-static char *
-process_bibtexline( char *p, str *tag, str *data, uchar stripquotes, fields *bibin, long nref, param *pm )
+#define KEEP_QUOTES  (0)
+#define STRIP_QUOTES (1)
+
+static int
+merge_tokens_into_data( str *data, slist *tokens, int stripquotes )
 {
-	int i, status;
-	slist tokens;
+	int i, esc_s;
 	str *s;
 
-	str_empty( data );
+	for ( i=0; i<tokens->n; i++ ) {
 
-	p = bibtex_tag( p, tag );
+		s     = slist_str( tokens, i );
+		esc_s = token_is_escaped( s );
 
-	if ( str_is_empty( tag ) ) {
-		/* ...skip this line */
-		while ( *p && *p!='\n' && *p!='\r' ) p++;
-		while ( *p=='\n' || *p=='\r' ) p++;
-		return p;
+		if ( ( esc_s == ESCAPED_BRACES ) ||
+		     ( stripquotes == STRIP_QUOTES && esc_s == ESCAPED_QUOTES ) ) {
+			str_trimbegin( s, 1 );
+			str_trimend( s, 1 );
+		}
+
+		str_strcat( data, s );
+
 	}
 
+	if ( str_memerr( data ) ) return BIBL_ERR_MEMERR;
+	else return BIBL_OK;
+}
+
+/* return NULL on memory error */
+static const char *
+process_bibtexline( const char *p, str *tag, str *data, uchar stripquotes, loc *currloc )
+{
+	slist tokens;
+	int status;
+
+	str_empty( data );
+
 	slist_init( &tokens );
 
-	if ( *p=='=' ) {
-		p = bibtex_data( p+1, bibin, &tokens, nref, pm );
-		if ( p==NULL ) goto out;
+	p = bibtex_tag( skip_ws( p ), tag );
+	if ( p ) {
+		if ( str_is_empty( tag ) ) {
+			p = skip_line( p );
+			goto out;
+		}
 	}
 
-	replace_strings( &tokens, bibin, pm );
+	if ( p && *p=='=' ) {
+		p = bibtex_data( p+1, &tokens, currloc );
+	}
 
-	status = string_concatenate( &tokens, bibin, nref, pm );
-	if ( status!=BIBL_OK ) {
-		p = NULL;
-		goto out;
+	if ( p ) {
+		status = replace_strings( &tokens );
+		if ( status!=BIBL_OK ) p = NULL;
 	}
 
-	for ( i=0; i<tokens.n; i++ ) {
-		s = slist_str( &tokens, i );
-		if ( ( stripquotes && s->data[0]=='\"' && s->data[s->len-1]=='\"' ) ||
-		     ( s->data[0]=='{' && s->data[s->len-1]=='}' ) ) {
-			str_trimbegin( s, 1 );
-			str_trimend( s, 1 );
-		}
-		str_strcat( data, slist_str( &tokens, i ) );
+	if ( p ) {
+		status = string_concatenate( &tokens, currloc );
+		if ( status!=BIBL_OK ) p = NULL;
 	}
+
+	if ( p ) {
+		status = merge_tokens_into_data( data, &tokens, stripquotes );
+		if ( status!=BIBL_OK ) p = NULL;
+	}
+
 out:
 	slist_free( &tokens );
 	return p;
 }
 
-/* process_cite()
+/* process_ref()
  *
  */
 static int
-process_cite( fields *bibin, char *p, char *filename, long nref, param *pm )
+process_ref( fields *bibin, const char *p, loc *currloc )
 {
 	int fstatus, status = BIBL_OK;
 	str type, id, tag, data;
@@ -402,21 +550,22 @@
 
 	if ( str_is_empty( &type ) || str_is_empty( &id ) ) goto out;
 
-	fstatus = fields_add( bibin, "INTERNAL_TYPE", str_cstr( &type ), 0 );
+	fstatus = fields_add( bibin, "INTERNAL_TYPE", str_cstr( &type ), LEVEL_MAIN );
 	if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 
-	fstatus = fields_add( bibin, "REFNUM", str_cstr( &id), 0 );
+	fstatus = fields_add( bibin, "REFNUM", str_cstr( &id ), LEVEL_MAIN );
 	if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 
 	while ( *p ) {
-		p = process_bibtexline( p, &tag, &data, 1, bibin, nref, pm );
+
+		p = process_bibtexline( p, &tag, &data, STRIP_QUOTES, currloc );
 		if ( p==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
-		/* no anonymous or empty fields allowed */
-		if ( str_has_value( &tag ) && str_has_value( &data ) ) {
-			fstatus = fields_add( bibin, str_cstr( &tag ), str_cstr( &data ), 0 );
-			if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-		}
-		strs_empty( &tag, &data, NULL );
+
+		if ( !str_has_value( &tag ) || !str_has_value( &data ) ) continue;
+
+		fstatus = fields_add( bibin, str_cstr( &tag ), str_cstr( &data ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
+
 	}
 out:
 	strs_free( &type, &id, &tag, &data, NULL );
@@ -435,32 +584,38 @@
  *
  */
 static int
-process_string( char *p, long nref, param *pm )
+process_string( const char *p, loc *currloc )
 {
 	int n, status = BIBL_OK;
 	str s1, s2, *t;
+
 	strs_init( &s1, &s2, NULL );
+
 	while ( *p && *p!='{' && *p!='(' ) p++;
 	if ( *p=='{' || *p=='(' ) p++;
-	p = process_bibtexline( skip_ws( p ), &s1, &s2, 0, NULL, nref, pm );
+
+	p = process_bibtexline( skip_ws( p ), &s1, &s2, KEEP_QUOTES, currloc );
 	if ( p==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
+
 	if ( str_has_value( &s2 ) ) {
 		str_findreplace( &s2, "\\ ", " " );
+	} else {
+		str_strcpyc( &s2, "" );
 	}
+
 	if ( str_has_value( &s1 ) ) {
 		n = slist_find( &find, &s1 );
 		if ( n==-1 ) {
-			t = slist_add( &find, &s1 );
-			if ( t==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
-			if ( str_has_value( &s2 ) ) t = slist_add( &replace, &s2 );
-			else t = slist_addc( &replace, "" );
-			if ( t==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
+			status = slist_add_ret( &find,    &s1, BIBL_OK, BIBL_ERR_MEMERR );
+			if ( status!=BIBL_OK ) goto out;
+			status = slist_add_ret( &replace, &s2, BIBL_OK, BIBL_ERR_MEMERR );
+			if ( status!=BIBL_OK ) goto out;
 		} else {
-			if ( str_has_value( &s2 ) ) t = slist_set( &replace, n, &s2 );
-			else t = slist_setc( &replace, n, "" );
+			t = slist_set( &replace, n, &s2 );
 			if ( t==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
 		}
 	}
+
 out:
 	strs_free( &s1, &s2, NULL );
 	return status;
@@ -471,16 +626,22 @@
  * Handle '@STRING', '@reftype', and ignore '@COMMENT'
  */
 static int
-bibtexin_processf( fields *bibin, char *data, char *filename, long nref, param *p )
+bibtexin_processf( fields *bibin, const char *data, const char *filename, long nref, param *pm )
 {
+	loc currloc;
+
+	currloc.progname = pm->progname;
+	currloc.filename = filename;
+	currloc.nref     = nref;
+
 	if ( !strncasecmp( data, "@STRING", 7 ) ) {
-		process_string( data+7, nref, p );
+		process_string( data+7, &currloc );
 		return 0;
 	} else if ( !strncasecmp( data, "@COMMENT", 8 ) ) {
 		/* Not sure if these are real Bibtex, but not references */
 		return 0;
 	} else {
-		process_cite( bibin, data, filename, nref, p );
+		process_ref( bibin, data, &currloc );
 		return 1;
 	}
 }
@@ -501,7 +662,7 @@
 bibtex_split( slist *tokens, str *s )
 {
 	int i, n = s->len, nbrackets = 0, status = BIBL_OK;
-	str tok, *t;
+	str tok;
 
 	str_init( &tok );
 
@@ -516,21 +677,15 @@
 			str_addchar( &tok, s->data[i] );
 		} else if ( is_ws( s->data[i] ) ) {
 			if ( str_has_value( &tok ) ) {
-				t = slist_add( tokens, &tok );
-				if ( !t ) {
-					status = BIBL_ERR_MEMERR;
-					goto out;
-				}
+				status = slist_add_ret( tokens, &tok, BIBL_OK, BIBL_ERR_MEMERR );
+				if ( status!=BIBL_OK ) goto out;
 			}
 			str_empty( &tok );
 		}
 	}
 	if ( str_has_value( &tok ) ) {
-		t = slist_add( tokens, &tok );
-		if ( !t ) {
-			status = BIBL_ERR_MEMERR;
-			goto out;
-		}
+		status = slist_add_ret( tokens, &tok, BIBL_OK, BIBL_ERR_MEMERR );
+		if ( status!=BIBL_OK ) goto out;
 	}
 
 	for ( i=0; i<tokens->n; ++i ) {
@@ -546,8 +701,8 @@
 bibtex_addtitleurl( fields *info, str *in )
 {
 	int fstatus, status = BIBL_OK;
+	const char *p;
 	str s;
-	char *p;
 
 	str_init( &s );
 
@@ -591,10 +746,12 @@
 static void
 bibtex_process_tilde( str *s )
 {
-	char *p, *q;
+	const char *p;
+	char *q;
 	int n = 0;
 
-	p = q = s->data;
+	p = s->data;
+	q = s->data;
 	if ( !p ) return;
 	while ( *p ) {
 		if ( *p=='~' ) {
@@ -817,7 +974,7 @@
 *****************************************************/
 
 static int
-bibtexin_typef( fields *bibin, char *filename, int nrefs, param *p )
+bibtexin_typef( fields *bibin, const char *filename, int nrefs, param *p )
 {
 	int ntypename, nrefname, is_default;
 	char *refname = "", *typename = "";
@@ -1127,8 +1284,8 @@
 bibtexin_keyword( fields *bibin, int m, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
 {
 	int fstatus, status = BIBL_OK;
+	const char *p;
 	str keyword;
-	char *p;
 
 	p = invalue->data;
 	str_init( &keyword );
diff --git a/bibutils/bibtexout.c b/bibutils/bibtexout.c
--- a/bibutils/bibtexout.c
+++ b/bibutils/bibtexout.c
@@ -1,7 +1,7 @@
 /*
  * bibtexout.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -15,38 +15,56 @@
 #include "utf8.h"
 #include "xml.h"
 #include "fields.h"
+#include "generic.h"
 #include "name.h"
 #include "title.h"
+#include "type.h"
 #include "url.h"
 #include "bibformats.h"
 
+/*****************************************************
+ PUBLIC: int bibtexout_initparams()
+*****************************************************/
+
 static int  bibtexout_write( fields *in, FILE *fp, param *p, unsigned long refnum );
-static void bibtexout_writeheader( FILE *outptr, param *p );
+static int  bibtexout_assemble( fields *in, fields *out, param *pm, unsigned long refnum );
 
-void
-bibtexout_initparams( param *p, const char *progname )
+int
+bibtexout_initparams( param *pm, const char *progname )
 {
-	p->writeformat      = BIBL_BIBTEXOUT;
-	p->format_opts      = 0;
-	p->charsetout       = BIBL_CHARSET_DEFAULT;
-	p->charsetout_src   = BIBL_SRC_DEFAULT;
-	p->latexout         = 1;
-	p->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
-	p->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
-	p->xmlout           = BIBL_XMLOUT_FALSE;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->singlerefperfile = 0;
+	pm->writeformat      = BIBL_BIBTEXOUT;
+	pm->format_opts      = 0;
+	pm->charsetout       = BIBL_CHARSET_DEFAULT;
+	pm->charsetout_src   = BIBL_SRC_DEFAULT;
+	pm->latexout         = 1;
+	pm->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
+	pm->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
+	pm->xmlout           = BIBL_XMLOUT_FALSE;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->singlerefperfile = 0;
 
-	p->headerf = bibtexout_writeheader;
-	p->footerf = NULL;
-	p->writef  = bibtexout_write;
+	pm->headerf   = generic_writeheader;
+	pm->footerf   = NULL;
+	pm->assemblef = bibtexout_assemble;
+	pm->writef    = bibtexout_write;
 
-	if ( !p->progname && progname )
-		p->progname = strdup( progname );
+	if ( !pm->progname ) {
+		if ( !progname ) pm->progname = NULL;
+		else {
+			pm->progname = strdup( progname );
+			if ( !pm->progname ) return BIBL_ERR_MEMERR;
+		}
+	}
+
+	return BIBL_OK;
 }
 
+/*****************************************************
+ PUBLIC: int bibtexout_assemble()
+*****************************************************/
+
 enum {
 	TYPE_UNKNOWN = 0,
 	TYPE_ARTICLE,
@@ -68,97 +86,61 @@
 };
 
 static int
-is_genre_element( fields *in, int n )
+bibtexout_type( fields *in, const char *progname, const char *filename, unsigned long refnum )
 {
-	char *tag;
-
-	tag = fields_tag( in, n, FIELDS_CHRP );
-
-	if ( !strcasecmp( tag, "GENRE:MARC"     ) ) return 1;
-	if ( !strcasecmp( tag, "GENRE:BIBUTILS" ) ) return 1;
-	if ( !strcasecmp( tag, "GENRE:UNKNOWN"  ) ) return 1;
-
-	return 0;
-}
+	match_type genre_matches[] = {
+		{ "periodical",             TYPE_ARTICLE,       LEVEL_ANY  },
+		{ "academic journal",       TYPE_ARTICLE,       LEVEL_ANY  },
+		{ "magazine",               TYPE_ARTICLE,       LEVEL_ANY  },
+		{ "newspaper",              TYPE_ARTICLE,       LEVEL_ANY  },
+		{ "article",                TYPE_ARTICLE,       LEVEL_ANY  },
+		{ "instruction",            TYPE_MANUAL,        LEVEL_ANY  },
+		{ "book",                   TYPE_BOOK,          LEVEL_MAIN },
+		{ "book",                   TYPE_INBOOK,        LEVEL_ANY  },
+		{ "book chapter",           TYPE_INBOOK,        LEVEL_ANY  },
+		{ "unpublished",            TYPE_UNPUBLISHED,   LEVEL_ANY  },
+		{ "manuscript",             TYPE_UNPUBLISHED,   LEVEL_ANY  },
+		{ "conference publication", TYPE_PROCEEDINGS,   LEVEL_MAIN },
+		{ "conference publication", TYPE_INPROCEEDINGS, LEVEL_ANY  },
+		{ "collection",             TYPE_COLLECTION,    LEVEL_MAIN },
+		{ "collection",             TYPE_INCOLLECTION,  LEVEL_ANY  },
+		{ "report",                 TYPE_REPORT,        LEVEL_ANY  },
+		{ "technical report",       TYPE_REPORT,        LEVEL_ANY  },
+		{ "Masters thesis",         TYPE_MASTERSTHESIS, LEVEL_ANY  },
+		{ "Diploma thesis",         TYPE_DIPLOMATHESIS, LEVEL_ANY  },
+		{ "Ph.D. thesis",           TYPE_PHDTHESIS,     LEVEL_ANY  },
+		{ "Licentiate thesis",      TYPE_PHDTHESIS,     LEVEL_ANY  },
+		{ "thesis",                 TYPE_PHDTHESIS,     LEVEL_ANY  },
+		{ "electronic",             TYPE_ELECTRONIC,    LEVEL_ANY  },
+		{ "miscellaneous",          TYPE_MISC,          LEVEL_ANY  },
+	};
+	int ngenre_matches = sizeof( genre_matches ) / sizeof( genre_matches[0] );
 
-static int
-is_issuance_element( fields *in, int n )
-{
-	if ( !strcasecmp( fields_tag( in, n, FIELDS_CHRP ), "ISSUANCE" ) ) return 1;
-	else return 0;
-}
+	match_type resource_matches[] = {
+		{ "moving image",           TYPE_ELECTRONIC,    LEVEL_ANY  },
+		{ "software, multimedia",   TYPE_ELECTRONIC,    LEVEL_ANY  },
+	};
+	int nresource_matches = sizeof( resource_matches ) /sizeof( resource_matches[0] );
 
-static int
-bibtexout_type( fields *in, char *filename, int refnum, param *p )
-{
-	int type = TYPE_UNKNOWN, i, maxlevel, n, level;
-	char *genre;
+	match_type issuance_matches[] = {
+		{ "monographic",            TYPE_BOOK,          LEVEL_MAIN },
+		{ "monographic",            TYPE_INBOOK,        LEVEL_ANY  },
+	};
+	int nissuance_matches = sizeof( issuance_matches ) / sizeof( issuance_matches[0] );
 
-	/* determine bibliography type from GENRE */
-	for ( i=0; i<in->n; ++i ) {
-		if ( !is_genre_element( in, i ) ) continue;
-		genre = fields_value( in, i, FIELDS_CHRP );
-		level = in->level[i];
-		if ( !strcasecmp( genre, "periodical" ) ||
-		     !strcasecmp( genre, "academic journal" ) ||
-		     !strcasecmp( genre, "magazine" ) ||
-		     !strcasecmp( genre, "newspaper" ) ||
-		     !strcasecmp( genre, "article" ) ) {
-			type = TYPE_ARTICLE;
-		} else if ( !strcasecmp( genre, "instruction" ) ) {
-			type = TYPE_MANUAL;
-		} else if ( !strcasecmp( genre, "unpublished" ) ||
-		            !strcasecmp( genre, "manuscript" ) ) {
-			type = TYPE_UNPUBLISHED;
-		} else if ( !strcasecmp( genre, "conference publication" ) ) {
-			if ( level==0 ) type=TYPE_PROCEEDINGS;
-			else type = TYPE_INPROCEEDINGS;
-		} else if ( !strcasecmp( genre, "collection" ) ) {
-			if ( level==0 ) type=TYPE_COLLECTION;
-			else type = TYPE_INCOLLECTION;
-		} else if ( !strcasecmp( genre, "report" ) ||
-		            !strcasecmp( genre, "technical report" ) ) {
-			type = TYPE_REPORT;
-		} else if ( !strcasecmp( genre, "book chapter" ) ) {
-			type = TYPE_INBOOK;
-		} else if ( !strcasecmp( genre, "book" ) ) {
-			if ( level==0 ) type = TYPE_BOOK;
-			else type = TYPE_INBOOK;
-		} else if ( !strcasecmp( genre, "thesis" ) ) {
-			if ( type==TYPE_UNKNOWN ) type=TYPE_PHDTHESIS;
-		} else if ( !strcasecmp( genre, "Ph.D. thesis" ) ) {
-			type = TYPE_PHDTHESIS;
-		} else if ( !strcasecmp( genre, "Licentiate thesis" ) ) {
-			type = TYPE_PHDTHESIS;
-		} else if ( !strcasecmp( genre, "Masters thesis" ) ) {
-			type = TYPE_MASTERSTHESIS;
-		} else if ( !strcasecmp( genre, "Diploma thesis" ) ) {
-			type = TYPE_DIPLOMATHESIS;
-		} else if ( !strcasecmp( genre, "electronic" ) ) {
-			type = TYPE_ELECTRONIC;
-		} else if ( !strcasecmp( genre, "miscellaneous" ) ) {
-			type = TYPE_MISC;
-		}
-	}
+	int type, maxlevel, n;
 
-	/* check type from ISSUANCE */
-	if ( type==TYPE_UNKNOWN ) {
-		for ( i=0; i<in->n; ++i ) {
-			if ( !is_issuance_element( in, i ) ) continue;
-			genre = fields_value( in, i, FIELDS_CHRP );
-			if ( !strcasecmp( genre, "monographic" ) ) {
-				if ( level==0 ) type = TYPE_BOOK;
-			}
-		}
-	}
+	type = type_from_mods_hints( in, TYPE_FROM_GENRE, genre_matches, ngenre_matches, TYPE_UNKNOWN );
+	if ( type==TYPE_UNKNOWN ) type = type_from_mods_hints( in, TYPE_FROM_RESOURCE, resource_matches, nresource_matches, TYPE_UNKNOWN );
+	if ( type==TYPE_UNKNOWN ) type = type_from_mods_hints( in, TYPE_FROM_ISSUANCE, issuance_matches, nissuance_matches, TYPE_UNKNOWN );
 
 	/* default to TYPE_MISC */
 	if ( type==TYPE_UNKNOWN ) {
 		maxlevel = fields_maxlevel( in );
 		if ( maxlevel > 0 ) type = TYPE_MISC;
 		else {
-			if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-			fprintf( stderr, "Cannot identify TYPE in reference %d ", refnum+1 );
+			if ( progname ) fprintf( stderr, "%s: ", progname );
+			fprintf( stderr, "Cannot identify TYPE in reference %lu ", refnum+1 );
 			n = fields_find( in, "REFNUM", LEVEL_ANY );
 			if ( n!=FIELDS_NOTFOUND ) 
 				fprintf( stderr, " %s", (char*) fields_value( in, n, FIELDS_CHRP ) );
@@ -170,73 +152,6 @@
 }
 
 static void
-output( FILE *fp, fields *out, int format_opts )
-{
-	int i, j, len, nquotes;
-	char *tag, *value, ch;
-
-	/* ...output type information "@article{" */
-	value = ( char * ) fields_value( out, 0, FIELDS_CHRP );
-	if ( !(format_opts & BIBL_FORMAT_BIBOUT_UPPERCASE) ) fprintf( fp, "@%s{", value );
-	else {
-		len = strlen( value );
-		fprintf( fp, "@" );
-		for ( i=0; i<len; ++i )
-			fprintf( fp, "%c", toupper((unsigned char)value[i]) );
-		fprintf( fp, "{" );
-	}
-
-	/* ...output refnum "Smith2001" */
-	value = ( char * ) fields_value( out, 1, FIELDS_CHRP );
-	fprintf( fp, "%s", value );
-
-	/* ...rest of the references */
-	for ( j=2; j<out->n; ++j ) {
-		nquotes = 0;
-		tag   = ( char * ) fields_tag( out, j, FIELDS_CHRP );
-		value = ( char * ) fields_value( out, j, FIELDS_CHRP );
-		fprintf( fp, ",\n" );
-		if ( format_opts & BIBL_FORMAT_BIBOUT_WHITESPACE ) fprintf( fp, "  " );
-		if ( !(format_opts & BIBL_FORMAT_BIBOUT_UPPERCASE ) ) fprintf( fp, "%s", tag );
-		else {
-			len = strlen( tag );
-			for ( i=0; i<len; ++i )
-				fprintf( fp, "%c", toupper((unsigned char)tag[i]) );
-		}
-		if ( format_opts & BIBL_FORMAT_BIBOUT_WHITESPACE ) fprintf( fp, " = \t" );
-		else fprintf( fp, "=" );
-
-		if ( format_opts & BIBL_FORMAT_BIBOUT_BRACKETS ) fprintf( fp, "{" );
-		else fprintf( fp, "\"" );
-
-		len = strlen( value );
-		for ( i=0; i<len; ++i ) {
-			ch = value[i];
-			if ( ch!='\"' ) fprintf( fp, "%c", ch );
-			else {
-				if ( format_opts & BIBL_FORMAT_BIBOUT_BRACKETS || ( i>0 && value[i-1]=='\\' ) )
-					fprintf( fp, "\"" );
-				else {
-					if ( nquotes % 2 == 0 )
-						fprintf( fp, "``" );
-					else    fprintf( fp, "\'\'" );
-					nquotes++;
-				}
-			}
-		}
-
-		if ( format_opts & BIBL_FORMAT_BIBOUT_BRACKETS ) fprintf( fp, "}" );
-		else fprintf( fp, "\"" );
-	}
-
-	/* ...finish reference */
-	if ( format_opts & BIBL_FORMAT_BIBOUT_FINALCOMMA ) fprintf( fp, "," );
-	fprintf( fp, "\n}\n\n" );
-
-	fflush( fp );
-}
-
-static void
 append_type( int type, fields *out, int *status )
 {
 	char *typenames[ NUM_TYPES ] = {
@@ -413,10 +328,10 @@
 
 static void
 append_people( fields *in, char *tag, char *ctag, char *atag,
-		char *bibtag, int level, fields *out, int format_opts )
+		char *bibtag, int level, fields *out, int format_opts, int *status )
 {
+	int i, npeople, person, corp, asis, fstatus;
 	str allpeople, oneperson;
-	int i, npeople, person, corp, asis;
 
 	strs_init( &allpeople, &oneperson, NULL );
 
@@ -449,7 +364,8 @@
 		}
 	}
 	if ( npeople ) {
-		fields_add( out, bibtag, allpeople.data, LEVEL_MAIN );
+		fstatus = fields_add( out, bibtag, allpeople.data, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	}
 
 	strs_free( &allpeople, &oneperson, NULL );
@@ -820,25 +736,25 @@
 }
 
 static int
-append_data( fields *in, fields *out, param *p, unsigned long refnum )
+bibtexout_assemble( fields *in, fields *out, param *pm, unsigned long refnum )
 {
 	int type, status = BIBL_OK;
 
-	type = bibtexout_type( in, "", refnum, p );
+	type = bibtexout_type( in, pm->progname, "", refnum );
 
 	append_type        ( type, out, &status );
-	append_citekey     ( in, out, p->format_opts, &status );
-	append_people      ( in, "AUTHOR",     "AUTHOR:CORP",     "AUTHOR:ASIS",     "author", 0, out, p->format_opts );
-	append_people      ( in, "EDITOR",     "EDITOR:CORP",     "EDITOR:ASIS",     "editor", -1, out, p->format_opts );
-	append_people      ( in, "TRANSLATOR", "TRANSLATOR:CORP", "TRANSLATOR:ASIS", "translator", -1, out, p->format_opts );
-	append_titles      ( in, type, out, p->format_opts, &status );
+	append_citekey     ( in, out, pm->format_opts, &status );
+	append_people      ( in, "AUTHOR",     "AUTHOR:CORP",     "AUTHOR:ASIS",     "author", LEVEL_MAIN, out, pm->format_opts, &status );
+	append_people      ( in, "EDITOR",     "EDITOR:CORP",     "EDITOR:ASIS",     "editor", LEVEL_ANY, out, pm->format_opts, &status );
+	append_people      ( in, "TRANSLATOR", "TRANSLATOR:CORP", "TRANSLATOR:ASIS", "translator", LEVEL_ANY, out, pm->format_opts, &status );
+	append_titles      ( in, type, out, pm->format_opts, &status );
 	append_date        ( in, out, &status );
 	append_simple      ( in, "EDITION",            "edition",   out, &status );
 	append_simple      ( in, "PUBLISHER",          "publisher", out, &status );
 	append_simple      ( in, "ADDRESS",            "address",   out, &status );
 	append_simple      ( in, "VOLUME",             "volume",    out, &status );
 	append_issue_number( in, out, &status );
-	append_pages       ( in, out, p->format_opts, &status );
+	append_pages       ( in, out, pm->format_opts, &status );
 	append_keywords    ( in, out, &status );
 	append_simple      ( in, "CONTENTS",           "contents",  out, &status );
 	append_simple      ( in, "ABSTRACT",           "abstract",  out, &status );
@@ -864,25 +780,75 @@
 	return status;
 }
 
+/*****************************************************
+ PUBLIC: int bibtexout_write()
+*****************************************************/
+
 static int
-bibtexout_write( fields *in, FILE *fp, param *p, unsigned long refnum )
+bibtexout_write( fields *out, FILE *fp, param *pm, unsigned long refnum )
 {
-	int status;
-	fields out;
+	int i, j, len, nquotes, format_opts = pm->format_opts;
+	char *tag, *value, ch;
 
-	fields_init( &out );
+	/* ...output type information "@article{" */
+	value = ( char * ) fields_value( out, 0, FIELDS_CHRP );
+	if ( !(format_opts & BIBL_FORMAT_BIBOUT_UPPERCASE) ) fprintf( fp, "@%s{", value );
+	else {
+		len = (value) ? strlen( value ) : 0;
+		fprintf( fp, "@" );
+		for ( i=0; i<len; ++i )
+			fprintf( fp, "%c", toupper((unsigned char)value[i]) );
+		fprintf( fp, "{" );
+	}
 
-	status = append_data( in, &out, p, refnum );
-	if ( status==BIBL_OK ) output( fp, &out, p->format_opts );
+	/* ...output refnum "Smith2001" */
+	value = ( char * ) fields_value( out, 1, FIELDS_CHRP );
+	fprintf( fp, "%s", value );
 
-	fields_free( &out );
+	/* ...rest of the references */
+	for ( j=2; j<out->n; ++j ) {
+		nquotes = 0;
+		tag   = ( char * ) fields_tag( out, j, FIELDS_CHRP );
+		value = ( char * ) fields_value( out, j, FIELDS_CHRP );
+		fprintf( fp, ",\n" );
+		if ( format_opts & BIBL_FORMAT_BIBOUT_WHITESPACE ) fprintf( fp, "  " );
+		if ( !(format_opts & BIBL_FORMAT_BIBOUT_UPPERCASE ) ) fprintf( fp, "%s", tag );
+		else {
+			len = strlen( tag );
+			for ( i=0; i<len; ++i )
+				fprintf( fp, "%c", toupper((unsigned char)tag[i]) );
+		}
+		if ( format_opts & BIBL_FORMAT_BIBOUT_WHITESPACE ) fprintf( fp, " = \t" );
+		else fprintf( fp, "=" );
 
-	return status;
-}
+		if ( format_opts & BIBL_FORMAT_BIBOUT_BRACKETS ) fprintf( fp, "{" );
+		else fprintf( fp, "\"" );
 
-static void
-bibtexout_writeheader( FILE *outptr, param *p )
-{
-	if ( p->utf8bom ) utf8_writebom( outptr );
-}
+		len = strlen( value );
+		for ( i=0; i<len; ++i ) {
+			ch = value[i];
+			if ( ch!='\"' ) fprintf( fp, "%c", ch );
+			else {
+				if ( format_opts & BIBL_FORMAT_BIBOUT_BRACKETS || ( i>0 && value[i-1]=='\\' ) )
+					fprintf( fp, "\"" );
+				else {
+					if ( nquotes % 2 == 0 )
+						fprintf( fp, "``" );
+					else    fprintf( fp, "\'\'" );
+					nquotes++;
+				}
+			}
+		}
 
+		if ( format_opts & BIBL_FORMAT_BIBOUT_BRACKETS ) fprintf( fp, "}" );
+		else fprintf( fp, "\"" );
+	}
+
+	/* ...finish reference */
+	if ( format_opts & BIBL_FORMAT_BIBOUT_FINALCOMMA ) fprintf( fp, "," );
+	fprintf( fp, "\n}\n\n" );
+
+	fflush( fp );
+
+	return BIBL_OK;
+}
diff --git a/bibutils/bibtextypes.c b/bibutils/bibtextypes.c
--- a/bibutils/bibtextypes.c
+++ b/bibutils/bibtextypes.c
@@ -1,7 +1,7 @@
 /*
  * bibtypes.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Program and source code released under the GPL version 2
  *
diff --git a/bibutils/bibutils.c b/bibutils/bibutils.c
--- a/bibutils/bibutils.c
+++ b/bibutils/bibutils.c
@@ -1,7 +1,7 @@
 /*
  * bibutils.c
  *
- * Copyright (c) Chris Putnam 2005-2018
+ * Copyright (c) Chris Putnam 2005-2019
  *
  * Source code released under the GPL version 2
  *
@@ -11,35 +11,39 @@
 #include "bibutils.h"
 #include "bibformats.h"
 
-void
+int
 bibl_initparams( param *p, int readmode, int writemode, char *progname )
 {
+	int status;
 
 	switch ( readmode ) {
-	case BIBL_BIBTEXIN:     bibtexin_initparams( p, progname ); break;
-	case BIBL_BIBLATEXIN:   biblatexin_initparams( p, progname ); break;
-	case BIBL_COPACIN:      copacin_initparams( p, progname ); break;
-	case BIBL_EBIIN:        ebiin_initparams( p, progname ); break;
-	case BIBL_ENDNOTEIN:    endin_initparams( p, progname ); break;
-	case BIBL_ENDNOTEXMLIN: endxmlin_initparams( p, progname ); break;
-	case BIBL_MEDLINEIN:    medin_initparams( p, progname ); break;
-	case BIBL_MODSIN:       modsin_initparams( p, progname ); break;
-	case BIBL_NBIBIN:       nbibin_initparams( p, progname ); break;
-	case BIBL_RISIN:        risin_initparams( p, progname ); break;
-	case BIBL_WORDIN:       wordin_initparams( p, progname ); break;
-	default: /* internal error */;
+	case BIBL_BIBTEXIN:     status = bibtexin_initparams  ( p, progname ); break;
+	case BIBL_BIBLATEXIN:   status = biblatexin_initparams( p, progname ); break;
+	case BIBL_COPACIN:      status = copacin_initparams   ( p, progname ); break;
+	case BIBL_EBIIN:        status = ebiin_initparams     ( p, progname ); break;
+	case BIBL_ENDNOTEIN:    status = endin_initparams     ( p, progname ); break;
+	case BIBL_ENDNOTEXMLIN: status = endxmlin_initparams  ( p, progname ); break;
+	case BIBL_MEDLINEIN:    status = medin_initparams     ( p, progname ); break;
+	case BIBL_MODSIN:       status = modsin_initparams    ( p, progname ); break;
+	case BIBL_NBIBIN:       status = nbibin_initparams    ( p, progname ); break;
+	case BIBL_RISIN:        status = risin_initparams     ( p, progname ); break;
+	case BIBL_WORDIN:       status = wordin_initparams    ( p, progname ); break;
+	default:                status = BIBL_ERR_BADINPUT;
 	}
 
+	if ( status!=BIBL_OK ) return status;
+
 	switch ( writemode ) {
-	case BIBL_ADSABSOUT:   adsout_initparams( p, progname ); break;
-	case BIBL_BIBTEXOUT:   bibtexout_initparams( p, progname ); break;
-	case BIBL_ENDNOTEOUT:  endout_initparams( p, progname ); break;
-	case BIBL_ISIOUT:      isiout_initparams( p, progname ); break;
-	case BIBL_MODSOUT:     modsout_initparams( p, progname ); break;
-	case BIBL_NBIBOUT:     nbibout_initparams( p, progname ); break;
-	case BIBL_RISOUT:      risout_initparams( p, progname ); break;
-	case BIBL_WORD2007OUT: wordout_initparams( p, progname ); break;
-	default: /* internal error */;
+	case BIBL_ADSABSOUT:   status = adsout_initparams   ( p, progname ); break;
+	case BIBL_BIBTEXOUT:   status = bibtexout_initparams( p, progname ); break;
+	case BIBL_ENDNOTEOUT:  status = endout_initparams   ( p, progname ); break;
+	case BIBL_ISIOUT:      status = isiout_initparams   ( p, progname ); break;
+	case BIBL_MODSOUT:     status = modsout_initparams  ( p, progname ); break;
+	case BIBL_NBIBOUT:     status = nbibout_initparams  ( p, progname ); break;
+	case BIBL_RISOUT:      status = risout_initparams   ( p, progname ); break;
+	case BIBL_WORD2007OUT: status = wordout_initparams  ( p, progname ); break;
+	default:               status = BIBL_ERR_BADINPUT;
 	}
-}
 
+	return status;
+}
diff --git a/bibutils/bibutils.h b/bibutils/bibutils.h
--- a/bibutils/bibutils.h
+++ b/bibutils/bibutils.h
@@ -1,7 +1,7 @@
 /*
  * bibutils.h
  *
- * Copyright (c) Chris Putnam 2005-2018
+ * Copyright (c) Chris Putnam 2005-2019
  *
  * Source code released under GPL version 2
  *
@@ -113,12 +113,13 @@
 
 
         int  (*readf)(FILE*,char*,int,int*,str*,str*,int*);
-        int  (*processf)(fields*,char*,char*,long,struct param*);
+        int  (*processf)(fields*,const char*,const char*,long,struct param*);
         int  (*cleanf)(bibl*,struct param*);
-        int  (*typef) (fields*,char*,int,struct param*);
+        int  (*typef) (fields*,const char*,int,struct param*);
         int  (*convertf)(fields*,fields*,int,struct param*);
         void (*headerf)(FILE*,struct param*);
         void (*footerf)(FILE*);
+	int  (*assemblef)(fields*,fields*,struct param*,unsigned long);
         int  (*writef)(fields*,FILE*,struct param*,unsigned long);
         variants *all;
         int  nall;
@@ -126,16 +127,15 @@
 
 } param;
 
-extern void bibl_initparams( param *p, int readmode, int writemode,
-	char *progname );
-extern void bibl_freeparams( param *p );
-extern int  bibl_readasis( param *p, char *filename );
-extern int  bibl_addtoasis( param *p, char *entry );
-extern int  bibl_readcorps( param *p, char *filename );
-extern int  bibl_addtocorps( param *p, char *entry );
-extern int  bibl_read( bibl *b, FILE *fp, char *filename, param *p );
-extern int  bibl_write( bibl *b, FILE *fp, param *p );
-extern void bibl_reporterr( int err );
+int  bibl_initparams( param *p, int readmode, int writemode, char *progname );
+void bibl_freeparams( param *p );
+int  bibl_readasis( param *p, char *filename );
+int  bibl_addtoasis( param *p, char *entry );
+int  bibl_readcorps( param *p, char *filename );
+int  bibl_addtocorps( param *p, char *entry );
+int  bibl_read( bibl *b, FILE *fp, char *filename, param *p );
+int  bibl_write( bibl *b, FILE *fp, param *p );
+void bibl_reporterr( int err );
 
 #ifdef __cplusplus
 }
diff --git a/bibutils/bltypes.c b/bibutils/bltypes.c
--- a/bibutils/bltypes.c
+++ b/bibutils/bltypes.c
@@ -1,8 +1,8 @@
 /*
  * bltypes.c
  *
- * Copyright (c) Chris Putnam 2008-2018
- * Copyright (c) Johannes Wilm 2010-2018
+ * Copyright (c) Chris Putnam 2008-2019
+ * Copyright (c) Johannes Wilm 2010-2019
  *
  * Program and source code released under the GPL version 2
  *
diff --git a/bibutils/bu_auth.c b/bibutils/bu_auth.c
--- a/bibutils/bu_auth.c
+++ b/bibutils/bu_auth.c
@@ -1,7 +1,7 @@
 /*
  * bu_auth.c
  *
- * Copyright (c) Chris Putnam 2017-2018
+ * Copyright (c) Chris Putnam 2017-2019
  *
  * Source code released under the GPL version 2
  */
diff --git a/bibutils/bu_auth.h b/bibutils/bu_auth.h
--- a/bibutils/bu_auth.h
+++ b/bibutils/bu_auth.h
@@ -3,7 +3,7 @@
  *
  * Recognize added vocabulary for genre terms added by bibutils.
  *
- * Copyright (c) Chris Putnam 2017-2018
+ * Copyright (c) Chris Putnam 2017-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/charsets.c b/bibutils/charsets.c
--- a/bibutils/charsets.c
+++ b/bibutils/charsets.c
@@ -1,7 +1,7 @@
 /*
  * charsets.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/charsets.h b/bibutils/charsets.h
--- a/bibutils/charsets.h
+++ b/bibutils/charsets.h
@@ -1,7 +1,7 @@
 /*
  * charsets.h
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/copacin.c b/bibutils/copacin.c
--- a/bibutils/copacin.c
+++ b/bibutils/copacin.c
@@ -1,7 +1,7 @@
 /*
  * copacin.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -27,36 +27,41 @@
 *****************************************************/
 
 static int copacin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int copacin_processf( fields *bibin, char *p, char *filename, long nref, param *pm );
+static int copacin_processf( fields *bibin, const char *p, const char *filename, long nref, param *pm );
 static int copacin_convertf( fields *bibin, fields *info, int reftype, param *pm );
 
-void
-copacin_initparams( param *p, const char *progname )
+int
+copacin_initparams( param *pm, const char *progname )
 {
-	p->readformat       = BIBL_COPACIN;
-	p->charsetin        = BIBL_CHARSET_DEFAULT;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 0;
-	p->xmlin            = 0;
-	p->utf8in           = 0;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->output_raw       = 0;
+	pm->readformat       = BIBL_COPACIN;
+	pm->charsetin        = BIBL_CHARSET_DEFAULT;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 0;
+	pm->xmlin            = 0;
+	pm->utf8in           = 0;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->output_raw       = 0;
 
-	p->readf    = copacin_readf;
-	p->processf = copacin_processf;
-	p->cleanf   = NULL;
-	p->typef    = NULL;
-	p->convertf = copacin_convertf;
-	p->all      = copac_all;
-	p->nall     = copac_nall;
+	pm->readf    = copacin_readf;
+	pm->processf = copacin_processf;
+	pm->cleanf   = NULL;
+	pm->typef    = NULL;
+	pm->convertf = copacin_convertf;
+	pm->all      = copac_all;
+	pm->nall     = copac_nall;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( !pm->progname ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 /*****************************************************
@@ -70,7 +75,7 @@
     character 4 = space
 */
 static int
-copacin_istag( char *buf )
+copacin_istag( const char *buf )
 {
 	if (! ((buf[0]>='A' && buf[0]<='Z')) || (buf[0]>='a' && buf[0]<='z') )
 		return 0;
@@ -109,21 +114,18 @@
 		if ( copacin_istag( p ) ) {
 			if ( inref ) str_addchar( reference, '\n' );
 			str_strcatc( reference, p );
-			str_empty( line );
 			inref = 1;
 		} else if ( inref ) {
-			if ( p ) {
-				/* copac puts tag only on 1st line */
+			/* copac puts tag only on 1st line */
+			if ( *p ) p++;
+			if ( *p ) p++;
+			if ( *p ) p++;
+			if ( *p ) {
 				str_addchar( reference, ' ' );
-				if ( *p ) p++;
-				if ( *p ) p++;
-				if ( *p ) p++;
 				str_strcatc( reference, p );
 			}
-			str_empty( line );
-		} else {
-			str_empty( line );
 		}
+		str_empty( line );
 	}
 	return haveref;
 }
@@ -132,8 +134,8 @@
  PUBLIC: int copacin_processf()
 *****************************************************/
 
-static char*
-copacin_addtag2( char *p, str *tag, str *data )
+static const char*
+copacin_addtag2( const char *p, str *tag, str *data )
 {
 	int  i;
 	i =0;
@@ -151,8 +153,8 @@
 	return p;
 }
 
-static char *
-copacin_nextline( char *p )
+static const char *
+copacin_nextline( const char *p )
 {
 	while ( *p && *p!='\n' && *p!='\r') p++;
 	while ( *p=='\n' || *p=='\r' ) p++;
@@ -160,7 +162,7 @@
 }
 
 static int
-copacin_processf( fields *copacin, char *p, char *filename, long nref, param *pm )
+copacin_processf( fields *copacin, const char *p, const char *filename, long nref, param *pm )
 {
 	str tag, data;
 	int status;
diff --git a/bibutils/copactypes.c b/bibutils/copactypes.c
--- a/bibutils/copactypes.c
+++ b/bibutils/copactypes.c
@@ -1,7 +1,7 @@
 /*
  * copactypes.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Program and source code released under the GPL version 2
  *
diff --git a/bibutils/ebiin.c b/bibutils/ebiin.c
--- a/bibutils/ebiin.c
+++ b/bibutils/ebiin.c
@@ -1,7 +1,7 @@
 /*
  * ebiin.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -19,40 +19,45 @@
 #include "bibformats.h"
 
 static int ebiin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int ebiin_processf( fields *ebiin, char *data, char *filename, long nref, param *p );
+static int ebiin_processf( fields *ebiin, const char *data, const char *filename, long nref, param *p );
 
 
 /*****************************************************
  PUBLIC: void ebiin_initparams()
 *****************************************************/
-void
-ebiin_initparams( param *p, const char *progname )
+int
+ebiin_initparams( param *pm, const char *progname )
 {
-	p->readformat       = BIBL_EBIIN;
-	p->charsetin        = BIBL_CHARSET_UNICODE;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 0;
-	p->xmlin            = 1;
-	p->utf8in           = 1;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->output_raw       = BIBL_RAW_WITHMAKEREFID |
-	                      BIBL_RAW_WITHCHARCONVERT;
+	pm->readformat       = BIBL_EBIIN;
+	pm->charsetin        = BIBL_CHARSET_UNICODE;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 0;
+	pm->xmlin            = 1;
+	pm->utf8in           = 1;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->output_raw       = BIBL_RAW_WITHMAKEREFID |
+	                       BIBL_RAW_WITHCHARCONVERT;
 
-	p->readf    = ebiin_readf;
-	p->processf = ebiin_processf;
-	p->cleanf   = NULL;
-	p->typef    = NULL;
-	p->convertf = NULL;
-	p->all      = NULL;
-	p->nall     = 0;
+	pm->readf    = ebiin_readf;
+	pm->processf = ebiin_processf;
+	pm->cleanf   = NULL;
+	pm->typef    = NULL;
+	pm->convertf = NULL;
+	pm->all      = NULL;
+	pm->nall     = 0;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( !pm->progname ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 /*****************************************************
@@ -154,7 +159,7 @@
  *             <MedlineDate>2003 Jan-Feb</MedlineDate>
  */
 static int
-ebiin_medlinedate_year( fields *info, char *p, int level, char **end )
+ebiin_medlinedate_year( fields *info, const char *p, int level, const char **end )
 {
 	int fstatus, status = BIBL_OK;
 	str s;
@@ -175,7 +180,7 @@
 	return status;
 }
 static int
-ebiin_medlinedate_month( fields *info, char *p, int level, char **end )
+ebiin_medlinedate_month( fields *info, const char *p, int level, const char **end )
 {
 	int fstatus, status = BIBL_OK;
 	str s;
@@ -198,7 +203,7 @@
 }
 
 static int
-ebiin_medlinedate_day( fields *info, char *p, int level, char **end )
+ebiin_medlinedate_day( fields *info, const char *p, int level, const char **end )
 {
 	int fstatus, status = BIBL_OK;
 	str s;
@@ -223,7 +228,7 @@
 ebiin_medlinedate( fields *info, xml *node, int level )
 {
 	int status = BIBL_OK;
-	char *p;
+	const char *p;
 
 	if ( !xml_has_value( node ) ) return status;
 
@@ -306,7 +311,7 @@
  * </Pagination>
  */
 static int
-ebiin_pages( fields *info, char *p )
+ebiin_pages( fields *info, const char *p )
 {
 	int i, status, ret = BIBL_OK;
 	const int level = 1;
@@ -744,7 +749,7 @@
 }
 
 static int
-ebiin_processf( fields *ebiin, char *data, char *filename, long nref, param *p )
+ebiin_processf( fields *ebiin, const char *data, const char *filename, long nref, param *p )
 {
 	int status;
 	xml top;
diff --git a/bibutils/endin.c b/bibutils/endin.c
--- a/bibutils/endin.c
+++ b/bibutils/endin.c
@@ -1,7 +1,7 @@
 /*
  * endin.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -27,38 +27,43 @@
 *****************************************************/
 
 static int endin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int endin_processf( fields *endin, char *p, char *filename, long nref, param *pm );
-int endin_typef( fields *endin, char *filename, int nrefs, param *p );
+static int endin_processf( fields *endin, const char *p, const char *filename, long nref, param *pm );
+int endin_typef( fields *endin, const char *filename, int nrefs, param *p );
 int endin_convertf( fields *endin, fields *info, int reftype, param *p );
 int endin_cleanf( bibl *bin, param *p );
 
-void
-endin_initparams( param *p, const char *progname )
+int
+endin_initparams( param *pm, const char *progname )
 {
-	p->readformat       = BIBL_ENDNOTEIN;
-	p->charsetin        = BIBL_CHARSET_DEFAULT;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 0;
-	p->xmlin            = 0;
-	p->utf8in           = 0;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->output_raw       = 0;
+	pm->readformat       = BIBL_ENDNOTEIN;
+	pm->charsetin        = BIBL_CHARSET_DEFAULT;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 0;
+	pm->xmlin            = 0;
+	pm->utf8in           = 0;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->output_raw       = 0;
 
-	p->readf    = endin_readf;
-	p->processf = endin_processf;
-	p->cleanf   = endin_cleanf;
-	p->typef    = endin_typef;
-	p->convertf = endin_convertf;
-	p->all      = end_all;
-	p->nall     = end_nall;
+	pm->readf    = endin_readf;
+	pm->processf = endin_processf;
+	pm->cleanf   = endin_cleanf;
+	pm->typef    = endin_typef;
+	pm->convertf = endin_convertf;
+	pm->all      = end_all;
+	pm->nall     = end_nall;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( !pm->progname ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 
@@ -72,7 +77,7 @@
     character 3 = space (ansi 32)
 */
 static int
-endin_istag( char *buf )
+endin_istag( const char *buf )
 {
 	const char others[]="!@#$^&*()+=?[~>";
 	if ( buf[0]!='%' ) return 0;
@@ -98,6 +103,7 @@
 	char *p;
 	*fcharset = CHARSET_UNKNOWN;
 	while ( !haveref && readmore( fp, buf, bufsize, bufpos, line ) ) {
+
 		if ( !line->data ) continue;
 		p = &(line->data[0]);
 
@@ -119,7 +125,7 @@
 			if ( reference->len ) str_addchar( reference, '\n' );
 			str_strcatc( reference, p );
 			inref = 1;
-		} else if ( inref && p ) {
+		} else if ( inref && *p ) {
 			str_addchar( reference, '\n' );
 			str_strcatc( reference, p );
 		}
@@ -132,8 +138,8 @@
 /*****************************************************
  PUBLIC: int endin_processf()
 *****************************************************/
-static char*
-process_endline( str *tag, str *data, char *p )
+static const char*
+process_endline( str *tag, str *data, const char *p )
 {
 	int  i;
 
@@ -153,8 +159,8 @@
 	return p;
 }
 
-static char *
-process_endline2( str *tag, str *data, char *p )
+static const char *
+process_endline2( str *tag, str *data, const char *p )
 {
 	while ( *p==' ' || *p=='\t' ) p++;
 	while ( *p && *p!='\r' && *p!='\n' )
@@ -165,7 +171,7 @@
 }
 
 static int
-endin_processf( fields *endin, char *p, char *filename, long nref, param *pm )
+endin_processf( fields *endin, const char *p, const char *filename, long nref, param *pm )
 {
 	str tag, data;
 	int status, n;
@@ -209,7 +215,7 @@
  * if !%B & !%J & !%R & !%I - journal article
  */
 int
-endin_typef( fields *endin, char *filename, int nrefs, param *p )
+endin_typef( fields *endin, const char *filename, int nrefs, param *p )
 {
 	int ntypename, nrefname, is_default, nj, nv, nb, nr, nt, ni;
 	char *refname = "", *typename="";
@@ -406,7 +412,7 @@
 		{ "DATE:MONTH", "PARTDATE:MONTH" },
 		{ "DATE:DAY",   "PARTDATE:DAY" }
 	};
-	char *p = invalue->data;
+	const char *p = invalue->data;
 	char month[10], *m;
 	int part, status;
 	str date;
diff --git a/bibutils/endout.c b/bibutils/endout.c
--- a/bibutils/endout.c
+++ b/bibutils/endout.c
@@ -1,7 +1,7 @@
 /*
  * endout.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -14,40 +14,60 @@
 #include "str.h"
 #include "strsearch.h"
 #include "fields.h"
+#include "generic.h"
 #include "name.h"
 #include "title.h"
+#include "type.h"
 #include "url.h"
 #include "bibformats.h"
 
-static int  endout_write( fields *in, FILE *fp, param *p, unsigned long refnum );
-static void endout_writeheader( FILE *outptr, param *p );
+/*****************************************************
+ PUBLIC: int endout_initparams()
+*****************************************************/
 
+static int endout_write( fields *in, FILE *fp, param *p, unsigned long refnum );
+static int endout_assemble( fields *in, fields *out, param *pm, unsigned long refnum );
 
-void
-endout_initparams( param *p, const char *progname )
+int
+endout_initparams( param *pm, const char *progname )
 {
-	p->writeformat      = BIBL_ENDNOTEOUT;
-	p->format_opts      = 0;
-	p->charsetout       = BIBL_CHARSET_DEFAULT;
-	p->charsetout_src   = BIBL_SRC_DEFAULT;
-	p->latexout         = 0;
-	p->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
-	p->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
-	p->xmlout           = BIBL_XMLOUT_FALSE;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->singlerefperfile = 0;
+	pm->writeformat      = BIBL_ENDNOTEOUT;
+	pm->format_opts      = 0;
+	pm->charsetout       = BIBL_CHARSET_DEFAULT;
+	pm->charsetout_src   = BIBL_SRC_DEFAULT;
+	pm->latexout         = 0;
+	pm->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
+	pm->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
+	pm->xmlout           = BIBL_XMLOUT_FALSE;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->singlerefperfile = 0;
 
-	if ( p->charsetout == BIBL_CHARSET_UNICODE ) {
-		p->utf8out = p->utf8bom = 1;
+	if ( pm->charsetout == BIBL_CHARSET_UNICODE ) {
+		pm->utf8out = pm->utf8bom = 1;
 	}
 
-	p->headerf = endout_writeheader;
-	p->footerf = NULL;
-	p->writef  = endout_write;
+	pm->headerf   = generic_writeheader;
+	pm->footerf   = NULL;
+	pm->assemblef = endout_assemble;
+	pm->writef    = endout_write;
+
+	if ( !pm->progname ) {
+		if ( !progname ) pm->progname = NULL;
+		else {
+			pm->progname = strdup( progname );
+			if ( !pm->progname ) return BIBL_ERR_MEMERR;
+		}
+	}
+
+	return BIBL_OK;
 }
 
+/*****************************************************
+ PUBLIC: int endout_assemble()
+*****************************************************/
+
 enum {
 	TYPE_UNKNOWN = 0,
 	TYPE_GENERIC,                     /* Generic */
@@ -142,233 +162,188 @@
 	}
 }
 
-typedef struct match_type {
-	char *name;
-	int type;
-} match_type;
+static void
+type_report_progress( param *p, const char *element_type, int type, unsigned long refnum )
+{
+	if ( p->verbose ) {
+		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
+		fprintf( stderr, "Type from %s element in reference %lu: ", element_type, refnum+1 );
+		write_type( stderr, type );
+		fprintf( stderr, "\n" );
+	}
+}
 
 static int
+type_from_default( fields *in, param *p, unsigned long refnum )
+{
+	int n, type;
+
+	/* default to chapter if host terms */
+	if ( fields_maxlevel( in ) > 0 ) type = TYPE_INBOOK;
+
+	/* default to generic if no host terms */
+	else type = TYPE_GENERIC;
+
+
+	if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
+	fprintf( stderr, "Cannot identify TYPE in reference %lu ", refnum+1 );
+	n = fields_find( in, "REFNUM", LEVEL_ANY );
+	if ( n!=FIELDS_NOTFOUND )
+		fprintf( stderr, " %s", (char *) fields_value( in, n, FIELDS_CHRP ) );
+	if ( type==TYPE_INBOOK )
+		fprintf( stderr, " (defaulting to book chapter)\n" );
+	else
+		fprintf( stderr, " (defaulting to generic)\n" );
+
+	return type;
+}
+
+static int
 get_type( fields *in, param *p, unsigned long refnum )
 {
 	/* Comment out TYPE_GENERIC entries as that is default, but
          * keep in source as record of mapping decision. */
-	match_type match_genres[] = {
+	match_type genre_matches[] = {
 		/* MARC Authority elements */
-		{ "art original",              TYPE_ARTWORK },
-		{ "art reproduction",          TYPE_ARTWORK },
-		{ "article",                   TYPE_ARTICLE },
-		{ "atlas",                     TYPE_MAP },
-		{ "autobiography",             TYPE_BOOK },
-/*		{ "bibliography",              TYPE_GENERIC },*/
-		{ "biography",                 TYPE_BOOK },
-		{ "book",                      TYPE_BOOK },
-/*		{ "calendar",                  TYPE_GENERIC },*/
-/*		{ "catalog",                   TYPE_GENERIC },*/
-		{ "chart",                     TYPE_CHARTTABLE },
-/*		{ "comic or graphic novel",    TYPE_GENERIC },*/
-/*		{ "comic strip",               TYPE_GENERIC },*/
-		{ "conference publication",    TYPE_PROCEEDINGS },
-		{ "database",                  TYPE_ONLINEDATABASE },
-/*		{ "dictionary",                TYPE_GENERIC },*/
-		{ "diorama",                   TYPE_ARTWORK },
-/*		{ "directory",                 TYPE_GENERIC },*/
-		{ "discography",               TYPE_AUDIOVISUAL },
-/*		{ "drama",                     TYPE_GENERIC },*/
-		{ "encyclopedia",              TYPE_BOOK },
-/*		{ "essay",                     TYPE_GENERIC }, */
-/*		{ "festschrift",               TYPE_GENERIC },*/
-		{ "fiction",                   TYPE_BOOK },
-		{ "filmography",               TYPE_FILMBROADCAST },
-		{ "filmstrip",                 TYPE_FILMBROADCAST },
-/*		{ "finding aid",               TYPE_GENERIC },*/
-/*		{ "flash card",                TYPE_GENERIC },*/
-		{ "folktale",                  TYPE_CLASSICALWORK },
-		{ "font",                      TYPE_ELECTRONIC },
-/*		{ "game",                      TYPE_GENERIC },*/
-		{ "government publication",    TYPE_GOVERNMENT },
-		{ "graphic",                   TYPE_FIGURE },
-		{ "globe",                     TYPE_MAP },
-/*		{ "handbook",                  TYPE_GENERIC },*/
-		{ "history",                   TYPE_BOOK },
-		{ "hymnal",                    TYPE_BOOK },
-/*		{ "humor, satire",             TYPE_GENERIC },*/
-/*		{ "index",                     TYPE_GENERIC },*/
-/*		{ "instruction",               TYPE_GENERIC },*/
-/*		{ "interview",                 TYPE_GENERIC },*/
-		{ "issue",                     TYPE_ARTICLE },
-		{ "journal",                   TYPE_ARTICLE },
-/*		{ "kit",                       TYPE_GENERIC },*/
-/*		{ "language instruction",      TYPE_GENERIC },*/
-/*		{ "law report or digest",      TYPE_GENERIC },*/
-/*		{ "legal article",             TYPE_GENERIC },*/
-		{ "legal case and case notes", TYPE_CASE },
-		{ "legislation",               TYPE_BILL },
-		{ "letter",                    TYPE_COMMUNICATION },
-		{ "loose-leaf",                TYPE_GENERIC },
-		{ "map",                       TYPE_MAP },
-/*		{ "memoir",                    TYPE_GENERIC },*/
-/*		{ "microscope slide",          TYPE_GENERIC },*/
-/*		{ "model",                     TYPE_GENERIC },*/
-		{ "motion picture",            TYPE_AUDIOVISUAL },
-		{ "multivolume monograph",     TYPE_BOOK },
-		{ "newspaper",                 TYPE_NEWSARTICLE },
-		{ "novel",                     TYPE_BOOK },
-/*		{ "numeric data",              TYPE_GENERIC },*/
-/*		{ "offprint",                  TYPE_GENERIC },*/
-		{ "online system or service",  TYPE_ELECTRONIC },
-		{ "patent",                    TYPE_PATENT },
-		{ "periodical",                TYPE_MAGARTICLE },
-		{ "picture",                   TYPE_ARTWORK },
-/*		{ "poetry",                    TYPE_GENERIC },*/
-		{ "programmed text",           TYPE_PROGRAM },
-/*		{ "realia",                    TYPE_GENERIC },*/
-		{ "rehearsal",                 TYPE_AUDIOVISUAL },
-/*		{ "remote sensing image",      TYPE_GENERIC },*/
-/*		{ "reporting",                 TYPE_GENERIC },*/
-		{ "report",                    TYPE_REPORT },
-/*		{ "review",                    TYPE_GENERIC },*/
-/*		{ "script",                    TYPE_GENERIC },*/
-/*		{ "series",                    TYPE_GENERIC },*/
-/*		{ "short story",               TYPE_GENERIC },*/
-/*		{ "slide",                     TYPE_GENERIC },*/
-		{ "sound",                     TYPE_AUDIOVISUAL },
-/*		{ "speech",                    TYPE_GENERIC },*/
-/*		{ "standard or specification", TYPE_GENERIC },*/
-/*		{ "statistics",                TYPE_GENERIC },*/
-/*		{ "survey of literature",      TYPE_GENERIC },*/
-		{ "technical drawing",         TYPE_ARTWORK },
-		{ "technical report",          TYPE_REPORT },
-		{ "thesis",                    TYPE_THESIS },
-/*		{ "toy",                       TYPE_GENERIC },*/
-/*		{ "transparency",              TYPE_GENERIC },*/
-/*		{ "treaty",                    TYPE_GENERIC },*/
-		{ "videorecording",            TYPE_AUDIOVISUAL },
-		{ "web site",                  TYPE_ELECTRONIC },
+		{ "art original",              TYPE_ARTWORK,            LEVEL_ANY  },
+		{ "art reproduction",          TYPE_ARTWORK,            LEVEL_ANY  },
+		{ "article",                   TYPE_ARTICLE,            LEVEL_ANY  },
+		{ "atlas",                     TYPE_MAP,                LEVEL_ANY  },
+		{ "autobiography",             TYPE_BOOK,               LEVEL_ANY  },
+/*		{ "bibliography",              TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "biography",                 TYPE_BOOK,               LEVEL_ANY  },
+		{ "book",                      TYPE_BOOK,               LEVEL_MAIN },
+		{ "book",                      TYPE_INBOOK,             LEVEL_ANY  },
+/*		{ "calendar",                  TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "catalog",                   TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "chart",                     TYPE_CHARTTABLE,         LEVEL_ANY  },
+/*		{ "comic or graphic novel",    TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "comic strip",               TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "conference publication",    TYPE_PROCEEDINGS,        LEVEL_ANY  },
+		{ "database",                  TYPE_ONLINEDATABASE,     LEVEL_ANY  },
+/*		{ "dictionary",                TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "diorama",                   TYPE_ARTWORK,            LEVEL_ANY  },
+/*		{ "directory",                 TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "discography",               TYPE_AUDIOVISUAL,        LEVEL_ANY  },
+/*		{ "drama",                     TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "encyclopedia",              TYPE_BOOK,               LEVEL_ANY  },
+/*		{ "essay",                     TYPE_GENERIC,            LEVEL_ANY  }, */
+		{ "festschrift",               TYPE_BOOK,               LEVEL_MAIN },
+		{ "festschrift",               TYPE_INBOOK,             LEVEL_ANY  },
+		{ "fiction",                   TYPE_BOOK,               LEVEL_ANY  },
+		{ "filmography",               TYPE_FILMBROADCAST,      LEVEL_ANY  },
+		{ "filmstrip",                 TYPE_FILMBROADCAST,      LEVEL_ANY  },
+/*		{ "finding aid",               TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "flash card",                TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "folktale",                  TYPE_CLASSICALWORK,      LEVEL_ANY  },
+		{ "font",                      TYPE_ELECTRONIC,         LEVEL_ANY  },
+/*		{ "game",                      TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "government publication",    TYPE_GOVERNMENT,         LEVEL_ANY  },
+		{ "graphic",                   TYPE_FIGURE,             LEVEL_ANY  },
+		{ "globe",                     TYPE_MAP,                LEVEL_ANY  },
+/*		{ "handbook",                  TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "history",                   TYPE_BOOK,               LEVEL_ANY  },
+		{ "hymnal",                    TYPE_BOOK,               LEVEL_MAIN },
+		{ "hymnal",                    TYPE_INBOOK,             LEVEL_ANY  },
+/*		{ "humor, satire",             TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "index",                     TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "instruction",               TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "interview",                 TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "issue",                     TYPE_ARTICLE,            LEVEL_ANY  },
+		{ "journal",                   TYPE_ARTICLE,            LEVEL_ANY  },
+/*		{ "kit",                       TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "language instruction",      TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "law report or digest",      TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "legal article",             TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "legal case and case notes", TYPE_CASE,               LEVEL_ANY  },
+		{ "legislation",               TYPE_BILL,               LEVEL_ANY  },
+		{ "letter",                    TYPE_COMMUNICATION,      LEVEL_ANY  },
+		{ "loose-leaf",                TYPE_GENERIC,            LEVEL_ANY  },
+		{ "map",                       TYPE_MAP,                LEVEL_ANY  },
+/*		{ "memoir",                    TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "microscope slide",          TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "model",                     TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "motion picture",            TYPE_AUDIOVISUAL,        LEVEL_ANY  },
+		{ "multivolume monograph",     TYPE_BOOK,               LEVEL_ANY  },
+		{ "newspaper",                 TYPE_NEWSARTICLE,        LEVEL_ANY  },
+		{ "novel",                     TYPE_BOOK,               LEVEL_ANY  },
+/*		{ "numeric data",              TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "offprint",                  TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "online system or service",  TYPE_ELECTRONIC,         LEVEL_ANY  },
+		{ "patent",                    TYPE_PATENT,             LEVEL_ANY  },
+		{ "picture",                   TYPE_ARTWORK,            LEVEL_ANY  },
+/*		{ "poetry",                    TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "programmed text",           TYPE_PROGRAM,            LEVEL_ANY  },
+/*		{ "realia",                    TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "rehearsal",                 TYPE_AUDIOVISUAL,        LEVEL_ANY  },
+/*		{ "remote sensing image",      TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "reporting",                 TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "report",                    TYPE_REPORT,             LEVEL_ANY  },
+/*		{ "review",                    TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "script",                    TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "series",                    TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "short story",               TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "slide",                     TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "sound",                     TYPE_AUDIOVISUAL,        LEVEL_ANY  },
+/*		{ "speech",                    TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "standard or specification", TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "statistics",                TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "survey of literature",      TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "technical drawing",         TYPE_ARTWORK,            LEVEL_ANY  },
+		{ "technical report",          TYPE_REPORT,             LEVEL_ANY  },
+/*		{ "toy",                       TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "transparency",              TYPE_GENERIC,            LEVEL_ANY  },*/
+/*		{ "treaty",                    TYPE_GENERIC,            LEVEL_ANY  },*/
+		{ "videorecording",            TYPE_AUDIOVISUAL,        LEVEL_ANY  },
+		{ "web site",                  TYPE_ELECTRONIC,         LEVEL_ANY  },
 		/* Non-MARC Authority elements */
-		{ "academic journal",          TYPE_ARTICLE },
-		{ "magazine",                  TYPE_MAGARTICLE },
-		{ "hearing",                   TYPE_HEARING },
-		{ "Ph.D. thesis",              TYPE_PHDTHESIS },
-		{ "Masters thesis",            TYPE_MASTERSTHESIS },
-		{ "Diploma thesis",            TYPE_DIPLOMATHESIS },
-		{ "Doctoral thesis",           TYPE_DOCTORALTHESIS },
-		{ "Habilitation thesis",       TYPE_HABILITATIONTHESIS },
-		{ "Licentiate thesis",         TYPE_LICENTIATETHESIS },
-		{ "communication",             TYPE_COMMUNICATION },
-		{ "manuscript",                TYPE_MANUSCRIPT },
-		{ "unpublished",               TYPE_UNPUBLISHED },
+		{ "academic journal",          TYPE_ARTICLE,            LEVEL_ANY  },
+		{ "collection",                TYPE_BOOK,               LEVEL_MAIN },
+		{ "collection",                TYPE_INBOOK,             LEVEL_ANY  },
+		{ "magazine",                  TYPE_MAGARTICLE,         LEVEL_ANY  },
+		{ "hearing",                   TYPE_HEARING,            LEVEL_ANY  },
+		{ "Ph.D. thesis",              TYPE_PHDTHESIS,          LEVEL_ANY  },
+		{ "Masters thesis",            TYPE_MASTERSTHESIS,      LEVEL_ANY  },
+		{ "Diploma thesis",            TYPE_DIPLOMATHESIS,      LEVEL_ANY  },
+		{ "Doctoral thesis",           TYPE_DOCTORALTHESIS,     LEVEL_ANY  },
+		{ "Habilitation thesis",       TYPE_HABILITATIONTHESIS, LEVEL_ANY  },
+		{ "Licentiate thesis",         TYPE_LICENTIATETHESIS,   LEVEL_ANY  },
+		{ "communication",             TYPE_COMMUNICATION,      LEVEL_ANY  },
+		{ "manuscript",                TYPE_MANUSCRIPT,         LEVEL_ANY  },
+		{ "unpublished",               TYPE_UNPUBLISHED,        LEVEL_ANY  },
+		/* Delayed MARC Authority elements */
+		{ "thesis",                    TYPE_THESIS,             LEVEL_ANY  },
+		{ "periodical",                TYPE_MAGARTICLE,         LEVEL_ANY  },
 	};
-	int nmatch_genres = sizeof( match_genres ) / sizeof( match_genres[0] );
+	int ngenre_matches = sizeof( genre_matches ) / sizeof( genre_matches[0] );
 
-	int i, j, n, maxlevel, type = TYPE_UNKNOWN;
-	char *tag, *data;
+	match_type resource_matches[] = {
+		{ "moving image",              TYPE_FILMBROADCAST,      LEVEL_ANY  },
+		{ "software, multimedia",      TYPE_PROGRAM,            LEVEL_ANY  },
+	};
+	int nresource_matches = sizeof( resource_matches ) / sizeof( resource_matches[0] );
 
-	/* Determine type from genre information */
-	for ( i=0; i<in->n; ++i ) {
-		tag = fields_tag( in, i, FIELDS_CHRP );
-		if ( strcasecmp( tag, "GENRE:MARC" )!=0 &&
-		     strcasecmp( tag, "GENRE:BIBUTILS" )!=0 &&
-		     strcasecmp( tag, "GENRE:UNKNOWN" )!=0 ) continue;
-		data = fields_value( in, i, FIELDS_CHRP );
-		for ( j=0; j<nmatch_genres; ++j ) {
-			if ( !strcasecmp( data, match_genres[j].name ) ) {
-				type = match_genres[j].type;
-				fields_setused( in, i );
-			}
-		}
-		if ( p->verbose ) {
-			if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-			fprintf( stderr, "Type from tag '%s' data '%s': ", tag, data );
-			write_type( stderr, type );
-			fprintf( stderr, "\n" );
-		}
-		if ( type==TYPE_UNKNOWN ) {
-			if ( !strcasecmp( data, "periodical" ) )
-				type = TYPE_ARTICLE;
-			else if ( !strcasecmp( data, "thesis" ) )
-				type = TYPE_THESIS;
-			else if ( !strcasecmp( data, "book" ) ) {
-				if ( in->level[i]==0 ) type = TYPE_BOOK;
-				else type = TYPE_INBOOK;
-			}
-			else if ( !strcasecmp( data, "collection" ) ) {
-				if ( in->level[i]==0 ) type = TYPE_BOOK;
-				else type = TYPE_INBOOK;
-			}
-			if ( type!=TYPE_UNKNOWN ) fields_setused( in, i );
-		}
-		/* the inbook type should be defined if 'book' in host */
-		if ( type==TYPE_BOOK && in->level[i]>0 ) type = TYPE_INBOOK;
-	}
-	if ( p->verbose ) {
-		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-		fprintf( stderr, "Type from genre element: " );
-		write_type( stderr, type );
-		fprintf( stderr, "\n" );
-	}
+	match_type issuance_matches[] = {
+		{ "monographic",               TYPE_BOOK,               LEVEL_MAIN },
+		{ "monographic",               TYPE_INBOOK,             LEVEL_ANY  },
+	};
+	int nissuance_matches = sizeof( issuance_matches ) / sizeof( issuance_matches[0] );
 
-	/* Determine from resource information */
-	if ( type==TYPE_UNKNOWN ) {
-		for ( i=0; i<in->n; ++i ) {
-			if ( strcasecmp( fields_tag( in, i, FIELDS_CHRP ), "RESOURCE" ) )
-				continue;
-			data = fields_value( in, i, FIELDS_CHRP );
-			if ( !strcasecmp( data, "moving image" ) )
-				type = TYPE_FILMBROADCAST;
-			else if ( !strcasecmp( data, "software, multimedia" ) )
-				type = TYPE_PROGRAM;
-			if ( type!=TYPE_UNKNOWN ) fields_setused( in, i );
-		}
-		if ( p->verbose ) {
-			if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-			fprintf( stderr, "Type from resource element: " );
-			write_type( stderr, type );
-			fprintf( stderr, "\n" );
-		}
-	}
+	int type;
 
-	/* Determine from issuance information */
-	if ( type==TYPE_UNKNOWN ) {
-		for ( i=0; i<in->n; ++i ) {
-			if ( strcasecmp( fields_tag( in, i, FIELDS_CHRP ), "ISSUANCE" ) )
-				continue;
-			data = fields_value( in, i, FIELDS_CHRP );
-			if ( !strcasecmp( data, "monographic" ) ) {
-				if ( in->level[i]==0 ) type = TYPE_BOOK;
-				else type = TYPE_INBOOK;
-			}
-		}
-		if ( p->verbose ) {
-			if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-			fprintf( stderr, "Type from issuance element: " );
-			write_type( stderr, type );
-			fprintf( stderr, "\n" );
-		}
-	}
+	type = type_from_mods_hints( in, TYPE_FROM_GENRE, genre_matches, ngenre_matches, TYPE_UNKNOWN );
+	type_report_progress( p, "genre", type, refnum );
+	if ( type!=TYPE_UNKNOWN ) return type;
 
-	/* default to generic or book chapter, depending on maxlevel */
-	if ( type==TYPE_UNKNOWN ) {
-		maxlevel = fields_maxlevel( in );
-		if ( maxlevel > 0 ) type = TYPE_INBOOK;
-		else {
-			if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-			fprintf( stderr, "Cannot identify TYPE in reference %lu ", refnum+1 );
-			n = fields_find( in, "REFNUM", LEVEL_ANY );
-			if ( n!=FIELDS_NOTFOUND )
-				fprintf( stderr, " %s", (char *) fields_value( in, n, FIELDS_CHRP ) );
-			fprintf( stderr, " (defaulting to generic)\n" );
-			type = TYPE_GENERIC;
-		}
-	}
+	type = type_from_mods_hints( in, TYPE_FROM_RESOURCE, resource_matches, nresource_matches, TYPE_UNKNOWN );
+	type_report_progress( p, "resource", type, refnum );
+	if ( type!=TYPE_UNKNOWN ) return type;
 
-	if ( p->verbose ) {
-		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-		fprintf( stderr, "Final type: " );
-		write_type( stderr, type );
-		fprintf( stderr, "\n" );
-	}
-	
-	return type;
+	type = type_from_mods_hints( in, TYPE_FROM_ISSUANCE, issuance_matches, nissuance_matches, TYPE_UNKNOWN );
+	type_report_progress( p, "issuance", type, refnum );
+	if ( type!=TYPE_UNKNOWN ) return type;
+
+	return type_from_default( in, p, refnum );
 }
 
 static void
@@ -673,15 +648,15 @@
 }
 
 static int
-append_data( fields *in, fields *out, param *p, unsigned long refnum )
+endout_assemble( fields *in, fields *out, param *pm, unsigned long refnum )
 {
 	int added, type, status = BIBL_OK;
 
 	fields_clearused( in );
 
-	type = get_type( in, p, refnum );
+	type = get_type( in, pm, refnum );
 
-	append_type( type, out, p, &status );
+	append_type( type, out, pm, &status );
 
 	added = append_title( in, "TITLE",      "SUBTITLE",      "%T", LEVEL_MAIN, out, &status );
 	if ( added==0 ) append_title( in, "SHORTTITLE", "SHORTSUBTITLE", "%T", LEVEL_MAIN, out, &status );
@@ -779,8 +754,12 @@
 	return status;
 }
 
-static void
-output( FILE *fp, fields *out )
+/*****************************************************
+ PUBLIC: int endout_write()
+*****************************************************/
+
+static int
+endout_write( fields *out, FILE *fp, param *pm, unsigned long refnum )
 {
 	int i;
 
@@ -793,25 +772,5 @@
 
 	fprintf( fp, "\n" );
 	fflush( fp );
-}
-
-static int
-endout_write( fields *in, FILE *fp, param *p, unsigned long refnum )
-{
-	int status;
-	fields out;
-
-	fields_init( &out );
-	status = append_data( in, &out, p, refnum );
-	if ( status==BIBL_OK ) output( fp, &out );
-	fields_free( &out );
-
-	return status;
-}
-
-static void
-endout_writeheader( FILE *outptr, param *p )
-{
-	if ( p->utf8bom ) utf8_writebom( outptr );
+	return BIBL_OK;
 }
-
diff --git a/bibutils/endtypes.c b/bibutils/endtypes.c
--- a/bibutils/endtypes.c
+++ b/bibutils/endtypes.c
@@ -1,7 +1,7 @@
 /*
  * endtypes.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -58,7 +58,7 @@
 	{ "%Z", "NOTES",           SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%^", "CAPTION",         SIMPLE,   LEVEL_MAIN },
 	{ "%[", "ACCESSDATE",      SIMPLE,   LEVEL_MAIN },
 	{ "%=", "LASTMODDATE",     SIMPLE,   LEVEL_MAIN },
@@ -100,7 +100,7 @@
 	{ "%4", "CUSTOM4",      NOTES,    LEVEL_MAIN },
 	{ "%#", "CUSTOM5",      NOTES,    LEVEL_MAIN },
 	{ "%$", "CUSTOM6",      NOTES,    LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",      SIMPLE,   LEVEL_MAIN },
 	{ "%G", "LANGUAGE",     SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",  SIMPLE,   LEVEL_MAIN },  /* physical location */
@@ -149,7 +149,7 @@
 	{ "%4", "CUSTOM4",      NOTES,    LEVEL_MAIN },
 	{ "%#", "CUSTOM5",      NOTES,    LEVEL_MAIN },
 	{ "%$", "CUSTOM6",      NOTES,    LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",SIMPLE,LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",SIMPLE,LEVEL_MAIN },
 	{ "%>", "PDFLINK",      SIMPLE, LEVEL_MAIN },
 	{ "%G", "LANGUAGE",     SIMPLE, LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",  SIMPLE, LEVEL_MAIN },  /* physical location */
@@ -199,7 +199,7 @@
 	{ "%$", "CUSTOM6",      NOTES,    LEVEL_MAIN },
 	{ "%G", "LANGUAGE",     SIMPLE, LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",  SIMPLE, LEVEL_MAIN },  /* physical location */
-	{ "%+", "AUTHORADDRESS",SIMPLE,LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",SIMPLE,LEVEL_MAIN },
 	{ "%>", "PDFLINK",      SIMPLE, LEVEL_MAIN },
 	{ "%^", "CAPTION",      SIMPLE, LEVEL_MAIN },
 	{ "%[", "ACCESSDATE",   SIMPLE, LEVEL_MAIN },
@@ -246,7 +246,7 @@
 	{ "%O", "NOTES",           NOTES,    LEVEL_MAIN },
 	{ "%U", "URL",             URL,      LEVEL_MAIN },
 	{ "%Z", "NOTES",           SIMPLE,   LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
@@ -296,7 +296,7 @@
 	{ "%O", "NOTES",           NOTES,    LEVEL_MAIN },
 	{ "%U", "URL",             URL,      LEVEL_MAIN },
 	{ "%Z", "NOTES",           SIMPLE,   LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
@@ -345,7 +345,7 @@
 	{ "%O", "NOTES",           NOTES,    LEVEL_MAIN },
 	{ "%U", "URL",             URL,      LEVEL_MAIN },
 	{ "%Z", "NOTES",           NOTES,    LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
@@ -386,7 +386,7 @@
 	{ "%4", "CUSTOM4",         NOTES,  LEVEL_MAIN },
 	{ "%#", "CUSTOM5",         NOTES,  LEVEL_MAIN },
 	{ "%$", "CUSTOM6",         NOTES,  LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE, LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE, LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE, LEVEL_MAIN },
 	{ "%G", "LANGUAGE",        SIMPLE, LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE, LEVEL_MAIN },  /* physical location */
@@ -427,7 +427,7 @@
 	{ "%#", "CUSTOM5",         NOTES,  LEVEL_MAIN },
 	{ "%$", "CUSTOM6",         NOTES,  LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE, LEVEL_MAIN },  /* physical location */
-	{ "%+", "AUTHORADDRESS",   SIMPLE, LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE, LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE, LEVEL_MAIN },
 	{ "%G", "LANGUAGE",        SIMPLE, LEVEL_MAIN },
 	{ "%^", "CAPTION",         SIMPLE, LEVEL_MAIN },
@@ -471,7 +471,7 @@
 	{ "%O", "NOTES",           NOTES,    LEVEL_MAIN },
 	{ "%U", "URL",             URL,      LEVEL_MAIN },
 	{ "%Z", "NOTES",           NOTES,    LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
@@ -518,7 +518,7 @@
 	{ "%4", "CUSTOM4",         NOTES,    LEVEL_MAIN },
 	{ "%#", "CUSTOM5",         NOTES,    LEVEL_MAIN },
 	{ "%$", "CUSTOM6",         NOTES,    LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
@@ -559,7 +559,7 @@
 	{ "%O", "NOTES",           NOTES,    LEVEL_MAIN },
 	{ "%U", "URL",             URL,      LEVEL_MAIN },
 	{ "%Z", "NOTES",           NOTES,    LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
@@ -597,7 +597,7 @@
 	{ "%#", "CUSTOM5",         SIMPLE,   LEVEL_MAIN },
 	{ "%$", "CUSTOM6",         SIMPLE,   LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",      TITLE,    LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
@@ -638,7 +638,7 @@
 	{ "%4", "CUSTOM4",         NOTES,    LEVEL_MAIN },
 	{ "%#", "CUSTOM5",         NOTES,    LEVEL_MAIN },
 	{ "%$", "CUSTOM6",         NOTES,    LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
@@ -680,7 +680,7 @@
 	{ "%4", "CUSTOM4",         NOTES,    LEVEL_MAIN },
 	{ "%#", "CUSTOM5",         NOTES,    LEVEL_MAIN },
 	{ "%$", "CUSTOM6",         NOTES,    LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
@@ -723,7 +723,7 @@
 	{ "%V", "VOLUME",          SIMPLE,   LEVEL_MAIN },
 	{ "%Z", "NOTES",           NOTES,    LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
 	{ "%^", "CAPTION",         SIMPLE,   LEVEL_MAIN },
@@ -764,7 +764,7 @@
 	{ "%V", "VOLUME",          SIMPLE,   LEVEL_MAIN },
 	{ "%Z", "NOTES",           NOTES,    LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
 	{ "%^", "CAPTION",         SIMPLE,   LEVEL_MAIN },
@@ -801,7 +801,7 @@
 	{ "%4", "CUSTOM4",         NOTES,    LEVEL_MAIN },
 	{ "%#", "CUSTOM5",         NOTES,    LEVEL_MAIN },
 	{ "%$", "CUSTOM6",         NOTES,    LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
@@ -841,7 +841,7 @@
 	{ "%4", "CUSTOM4",         NOTES,    LEVEL_MAIN },
 	{ "%#", "CUSTOM5",         NOTES,    LEVEL_MAIN },
 	{ "%$", "CUSTOM6",         NOTES,    LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
@@ -879,7 +879,7 @@
 	{ "%4", "CUSTOM4",         SIMPLE,   LEVEL_MAIN },
 	{ "%#", "CUSTOM5",         SIMPLE,   LEVEL_MAIN },
 	{ "%$", "CUSTOM6",         SIMPLE,   LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
@@ -919,7 +919,7 @@
 	{ "%4", "CUSTOM4",         SIMPLE,   LEVEL_MAIN },
 	{ "%#", "CUSTOM5",         SIMPLE,   LEVEL_MAIN },
 	{ "%$", "CUSTOM6",         SIMPLE,   LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
@@ -1139,7 +1139,7 @@
 	{ "%U", "URL",             URL,      LEVEL_MAIN },
 	{ "%Z", "NOTES",           NOTES,    LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%>", "PDFLINK",         SIMPLE,   LEVEL_MAIN },
 	{ "%^", "CAPTION",         SIMPLE,   LEVEL_MAIN },
 	{ "%[", "ACCESSDATE",      SIMPLE,   LEVEL_MAIN },
@@ -1194,7 +1194,7 @@
 	{ "%Z", "NOTES",           NOTES,    LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",     SIMPLE,   LEVEL_MAIN },  /* physical location */
 	{ "%G", "LANGUAGE",        SIMPLE,   LEVEL_MAIN },
-	{ "%+", "AUTHORADDRESS",   SIMPLE,   LEVEL_MAIN },
+	{ "%+", "ADDRESS:AUTHOR",   SIMPLE,   LEVEL_MAIN },
 	{ "%^", "CAPTION",         SIMPLE,   LEVEL_MAIN },
 	{ "%[", "ACCESSDATE",      SIMPLE,   LEVEL_MAIN },
 	{ "%=", "LASTMODDATE",     SIMPLE,   LEVEL_MAIN },
diff --git a/bibutils/endxmlin.c b/bibutils/endxmlin.c
--- a/bibutils/endxmlin.c
+++ b/bibutils/endxmlin.c
@@ -1,7 +1,7 @@
 /*
  * endxmlin.c
  *
- * Copyright (c) Chris Putnam 2006-2018
+ * Copyright (c) Chris Putnam 2006-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -26,8 +26,8 @@
 extern int end_nall;
 
 static int endxmlin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int endxmlin_processf( fields *endin, char *p, char *filename, long nref, param *pm );
-extern int endin_typef( fields *endin, char *filename, int nrefs, param *p );
+static int endxmlin_processf( fields *endin, const char *p, const char *filename, long nref, param *pm );
+extern int endin_typef( fields *endin, const char *filename, int nrefs, param *p );
 extern int endin_convertf( fields *endin, fields *info, int reftype, param *p );
 extern int endin_cleanf( bibl *bin, param *p );
 
@@ -35,33 +35,38 @@
 /*****************************************************
  PUBLIC: void endxmlin_initparams()
 *****************************************************/
-void
-endxmlin_initparams( param *p, const char *progname )
+int
+endxmlin_initparams( param *pm, const char *progname )
 {
-	p->readformat       = BIBL_ENDNOTEXMLIN;
-	p->charsetin        = BIBL_CHARSET_DEFAULT;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 0;
-	p->xmlin            = 1;
-	p->utf8in           = 1;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->output_raw       = 0;
+	pm->readformat       = BIBL_ENDNOTEXMLIN;
+	pm->charsetin        = BIBL_CHARSET_DEFAULT;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 0;
+	pm->xmlin            = 1;
+	pm->utf8in           = 1;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->output_raw       = 0;
 
-	p->readf    = endxmlin_readf;
-	p->processf = endxmlin_processf;
-	p->cleanf   = NULL;
-	p->typef    = endin_typef;
-	p->convertf = endin_convertf;
-	p->all      = end_all;
-	p->nall     = end_nall;
+	pm->readf    = endxmlin_readf;
+	pm->processf = endxmlin_processf;
+	pm->cleanf   = NULL;
+	pm->typef    = endin_typef;
+	pm->convertf = endin_convertf;
+	pm->all      = end_all;
+	pm->nall     = end_nall;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( !pm->progname ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 /*****************************************************
@@ -78,19 +83,25 @@
 static int
 endxmlin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
-	str tmp;
-	char *startptr = NULL, *endptr = NULL;
 	int haveref = 0, inref = 0, done = 0, file_charset = CHARSET_UNKNOWN, m;
+	char *startptr = NULL, *endptr = NULL;
+	str tmp;
+
 	str_init( &tmp );
+
 	while ( !haveref && !done ) {
-		if ( line->data ) {
-			if ( !inref ) {
-				startptr = xml_find_start( line->data, "RECORD" );
-				if ( startptr ) inref = 1;
-			} else
-				endptr = xml_find_end( line->data, "RECORD" );
+
+		if ( !line->data ) {
+			done = xml_readmore( fp, buf, bufsize, bufpos );
+			str_strcatc( line, buf );
 		}
 
+		if ( !inref ) {
+			startptr = xml_find_start( line->data, "RECORD" );
+			if ( startptr ) inref = 1;
+		}
+		else    endptr = xml_find_end( line->data, "RECORD" );
+
 		/* If no <record> tag, we can trim up to last 8 bytes */
 		/* Emptying string can lose fragments of <record> tag */
 		if ( !startptr ) {
@@ -102,11 +113,13 @@
 			}
 		}
 
+		/* ...entire reference is not in line, read more */
 		if ( !startptr || !endptr ) {
 			done = xml_readmore( fp, buf, bufsize, bufpos );
 			str_strcatc( line, buf );
-		} else {
-			/* we can reallocate in the str_strcat, so re-find */
+		}
+		/* ...we can reallocate in str_strcat; must re-find the tags */
+		else {
 			startptr = xml_find_start( line->data, "RECORD" );
 			endptr = xml_find_end( line->data, "RECORD" );
 			str_segcpy( reference, startptr, endptr );
@@ -115,13 +128,15 @@
 			str_strcpy( line, &tmp );
 			haveref = 1;
 		}
-		if ( line->data ) {
-			m = xml_getencoding( line );
-			if ( m!=CHARSET_UNKNOWN ) file_charset = m;
-		}
+
+		m = xml_getencoding( line );
+		if ( m!=CHARSET_UNKNOWN ) file_charset = m;
+
 	}
+
 	str_free( &tmp );
 	*fcharset = file_charset;
+
 	return haveref;
 }
 
@@ -557,7 +572,7 @@
  * as the tags used in the Refer format output
  */
 static int
-endxmlin_processf( fields *fin, char *data, char *filename, long nref, param *pm )
+endxmlin_processf( fields *fin, const char *data, const char *filename, long nref, param *pm )
 {
 	int status;
 	xml top;
diff --git a/bibutils/entities.c b/bibutils/entities.c
--- a/bibutils/entities.c
+++ b/bibutils/entities.c
@@ -1,7 +1,7 @@
 /*
  * entities.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/entities.h b/bibutils/entities.h
--- a/bibutils/entities.h
+++ b/bibutils/entities.h
@@ -1,7 +1,7 @@
 /*
  * entities.h
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/fields.c b/bibutils/fields.c
--- a/bibutils/fields.c
+++ b/bibutils/fields.c
@@ -1,7 +1,7 @@
 /*
  * fields.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
@@ -90,8 +90,8 @@
 
 	newtags = (str*) realloc( f->tag, sizeof(str) * alloc );
 	newdata = (str*) realloc( f->data, sizeof(str) * alloc );
-	newused = (int*)    realloc( f->used, sizeof(int) * alloc );
-	newlevel= (int*)    realloc( f->level, sizeof(int) * alloc );
+	newused = (int*) realloc( f->used, sizeof(int) * alloc );
+	newlevel= (int*) realloc( f->level, sizeof(int) * alloc );
 
 	if ( newtags )  f->tag   = newtags;
 	if ( newdata )  f->data  = newdata;
@@ -112,7 +112,7 @@
 }
 
 int
-_fields_add( fields *f, char *tag, char *data, int level, int mode )
+_fields_add( fields *f, const char *tag, const char *data, int level, int mode )
 {
 	int i, n, status;
 	str *t, *d;
@@ -154,8 +154,8 @@
 }
 
 int
-_fields_add_tagsuffix( fields *f, char *tag, char *suffix,
-		char *data, int level, int mode )
+_fields_add_tagsuffix( fields *f, const char *tag, const char *suffix,
+		const char *data, int level, int mode )
 {
 	str newtag;
 	int ret;
@@ -189,28 +189,28 @@
  *
  */
 int
-fields_match_tag( fields *info, int n, char *tag )
+fields_match_tag( fields *info, int n, const char *tag )
 {
 	if ( !strcmp( fields_tag( info, n, FIELDS_CHRP ), tag ) ) return 1;
 	return 0;
 }
 
 int
-fields_match_casetag( fields *info, int n, char *tag )
+fields_match_casetag( fields *info, int n, const char *tag )
 {
 	if ( !strcasecmp( fields_tag( info, n, FIELDS_CHRP ), tag ) ) return 1;
 	return 0;
 }
 
 int
-fields_match_tag_level( fields *info, int n, char *tag, int level )
+fields_match_tag_level( fields *info, int n, const char *tag, int level )
 {
 	if ( !fields_match_level( info, n, level ) ) return 0;
 	return fields_match_tag( info, n, tag );
 }
 
 int
-fields_match_casetag_level( fields *info, int n, char *tag, int level )
+fields_match_casetag_level( fields *info, int n, const char *tag, int level )
 {
 	if ( !fields_match_level( info, n, level ) ) return 0;
 	return fields_match_casetag( info, n, tag );
@@ -218,11 +218,11 @@
 
 /* fields_find()
  *
- * Return position [0,f->n) for match of the tag.
+ * Return position [0,f->n) for first match of the tag.
  * Return FIELDS_NOTFOUND if tag isn't found.
  */
 int
-fields_find( fields *f, char *tag, int level )
+fields_find( fields *f, const char *tag, int level )
 {
 	int i;
 
@@ -277,7 +277,7 @@
  * return FIELDS_OK on success, FIELDS_ERR on memory error
  */
 int
-fields_replace_or_add( fields *f, char *tag, char *data, int level )
+fields_replace_or_add( fields *f, const char *tag, const char *data, int level )
 {
 	int n = fields_find( f, tag, level );
 	if ( n==FIELDS_NOTFOUND ) return fields_add( f, tag, data, level );
@@ -389,7 +389,7 @@
 }
 
 void *
-fields_findv( fields *f, int level, int mode, char *tag )
+fields_findv( fields *f, int level, int mode, const char *tag )
 {
 	int i, found = FIELDS_NOTFOUND;
 	intptr_t retn;
@@ -468,7 +468,7 @@
 }
 
 int
-fields_findv_each( fields *f, int level, int mode, vplist *a, char *tag )
+fields_findv_each( fields *f, int level, int mode, vplist *a, const char *tag )
 {
 	int i, status;
 
diff --git a/bibutils/fields.h b/bibutils/fields.h
--- a/bibutils/fields.h
+++ b/bibutils/fields.h
@@ -1,7 +1,7 @@
 /*
  * fields.h
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
@@ -46,14 +46,14 @@
 #define fields_add_tagsuffix( a, b, c, d, e )         _fields_add( a, b, c, d, e, FIELDS_NO_DUPS )
 #define fields_add_tagsuffix_can_dup( a, b, c, d, e ) _fields_add( a, b, c, d, e, FIELDS_CAN_DUP )
 
-int  _fields_add( fields *f, char *tag, char *data, int level, int mode );
-int  _fields_add_tagsuffix( fields *f, char *tag, char *suffix,
-		char *data, int level, int mode );
+int  _fields_add( fields *f, const char *tag, const char *data, int level, int mode );
+int  _fields_add_tagsuffix( fields *f, const char *tag, const char *suffix,
+		const char *data, int level, int mode );
 
 int  fields_maxlevel( fields *f );
 void fields_clearused( fields *f );
 void fields_setused( fields *f, int n );
-int  fields_replace_or_add( fields *f, char *tag, char *data, int level );
+int  fields_replace_or_add( fields *f, const char *tag, const char *data, int level );
 
 int fields_num( fields *f );
 int fields_used( fields *f, int n );
@@ -61,10 +61,10 @@
 int fields_nodata( fields *f, int n );
 
 int fields_match_level( fields *f, int n, int level );
-int fields_match_tag( fields *f, int n, char *tag );
-int fields_match_casetag( fields *f, int n, char *tag );
-int fields_match_tag_level( fields *f, int n, char *tag, int level );
-int fields_match_casetag_level( fields *f, int n, char *tag, int level );
+int fields_match_tag( fields *f, int n, const char *tag );
+int fields_match_casetag( fields *f, int n, const char *tag );
+int fields_match_tag_level( fields *f, int n, const char *tag, int level );
+int fields_match_casetag_level( fields *f, int n, const char *tag, int level );
 
 void fields_report( fields *f, FILE *fp );
 
@@ -86,12 +86,12 @@
 void *fields_value( fields *f, int n, int mode );
 int   fields_level( fields *f, int n );
  
-int   fields_find( fields *f, char *searchtag, int level );
+int   fields_find( fields *f, const char *searchtag, int level );
 
-void *fields_findv( fields *f, int level, int mode, char *tag );
+void *fields_findv( fields *f, int level, int mode, const char *tag );
 void *fields_findv_firstof( fields *f, int level, int mode, ... );
 
-int   fields_findv_each( fields *f, int level, int mode, vplist *a, char *tag );
+int   fields_findv_each( fields *f, int level, int mode, vplist *a, const char *tag );
 int   fields_findv_eachof( fields *f, int level, int mode, vplist *a, ... );
 
 #endif
diff --git a/bibutils/gb18030.h b/bibutils/gb18030.h
--- a/bibutils/gb18030.h
+++ b/bibutils/gb18030.h
@@ -1,7 +1,7 @@
 /*
  * gb18030.h
  *
- * Copyright (c) Chris Putnam 2008-2018
+ * Copyright (c) Chris Putnam 2008-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/generic.c b/bibutils/generic.c
--- a/bibutils/generic.c
+++ b/bibutils/generic.c
@@ -1,6 +1,6 @@
 /* generic.c
  *
- * Copyright (c) Chris Putnam 2016-2018
+ * Copyright (c) Chris Putnam 2016-2019
  *
  * Source code released under GPL version 2
  *
@@ -14,6 +14,7 @@
 #include "serialno.h"
 #include "title.h"
 #include "url.h"
+#include "utf8.h"
 #include "generic.h"
 
 /* stub for processtypes that aren't used, such as DEFAULT and ALWAYS handled by bibcore.c  */
@@ -96,4 +97,10 @@
 
 	if ( status == FIELDS_OK ) return BIBL_OK;
 	else return BIBL_ERR_MEMERR;
+}
+
+void
+generic_writeheader( FILE *outptr, param *pm )
+{
+	if ( pm->utf8bom ) utf8_writebom( outptr );
 }
diff --git a/bibutils/generic.h b/bibutils/generic.h
--- a/bibutils/generic.h
+++ b/bibutils/generic.h
@@ -1,6 +1,6 @@
 /* generic.h
  *
- * Copyright (c) Chris Putnam 2016-2018
+ * Copyright (c) Chris Putnam 2016-2019
  *
  * Source code released under GPL version 2
  *
@@ -8,6 +8,7 @@
 #ifndef GENERIC_H
 #define GENERIC_H
 
+#include <stdio.h>
 #include "bibutils.h"
 
 int generic_null    ( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout );
@@ -20,5 +21,7 @@
 int generic_skip    ( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout );
 int generic_title   ( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout );
 int generic_genre   ( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout );
+
+void generic_writeheader( FILE *outptr, param *pm );
 
 #endif
diff --git a/bibutils/intlist.c b/bibutils/intlist.c
--- a/bibutils/intlist.c
+++ b/bibutils/intlist.c
@@ -1,7 +1,7 @@
 /*
  * intlist.c
  *
- * Copyright (c) Chris Putnam 2007-2018
+ * Copyright (c) Chris Putnam 2007-2019
  *
  * Version 1/12/2017
  *
diff --git a/bibutils/intlist.h b/bibutils/intlist.h
--- a/bibutils/intlist.h
+++ b/bibutils/intlist.h
@@ -1,7 +1,7 @@
 /*
  * intlist.h
  *
- * Copyright (c) Chris Putnam 2007-2018
+ * Copyright (c) Chris Putnam 2007-2019
  *
  * Version 01/12/2017
  *
diff --git a/bibutils/is_ws.c b/bibutils/is_ws.c
--- a/bibutils/is_ws.c
+++ b/bibutils/is_ws.c
@@ -1,7 +1,7 @@
 /*
  * is_ws.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
@@ -10,14 +10,14 @@
 
 /* is_ws(), is whitespace */
 int 
-is_ws( char ch )
+is_ws( const char ch )
 {
-	if (ch==' ' || ch=='\n' || ch=='\t' || ch=='\r' ) return 1;
+	if ( ch==' ' || ch=='\n' || ch=='\t' || ch=='\r' ) return 1;
 	else return 0;
 }
 
-char *
-skip_ws( char *p )
+const char *
+skip_ws( const char *p )
 {
 	if ( p ) {
 		while ( is_ws( *p ) ) p++;
@@ -25,11 +25,24 @@
 	return p;
 }
 
-char *
-skip_notws( char *p )
+const char *
+skip_notws( const char *p )
 {
 	if ( p ) {
 		while ( *p && !is_ws( *p ) ) p++;
 	}
+	return p;
+}
+
+const char *
+skip_line( const char *p )
+{
+	/* ...skip until end-of-line markers */
+	while ( *p && *p!='\n' && *p!='\r' ) p++;
+
+	/* ...skip end-of-line marker */
+	if ( *p=='\r' ) p++; /* for CR LF or just CR end of lines */
+	if ( *p=='\n' ) p++;
+
 	return p;
 }
diff --git a/bibutils/is_ws.h b/bibutils/is_ws.h
--- a/bibutils/is_ws.h
+++ b/bibutils/is_ws.h
@@ -1,7 +1,7 @@
 /*
  * is_ws.h
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
@@ -9,9 +9,10 @@
 #ifndef IS_WS_H
 #define IS_WS_H
 
-extern int is_ws( char ch );
-extern char *skip_ws( char *p );
-extern char *skip_notws( char *p );
+int is_ws( const char ch );
+const char *skip_ws( const char *p );
+const char *skip_notws( const char *p );
+const char *skip_line( const char *p );
 
 #endif
 
diff --git a/bibutils/isiin.c b/bibutils/isiin.c
--- a/bibutils/isiin.c
+++ b/bibutils/isiin.c
@@ -1,7 +1,7 @@
 /*
  * isiin.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -9,6 +9,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
 #include "is_ws.h"
 #include "str.h"
 #include "str_conv.h"
@@ -22,41 +23,46 @@
 extern int isi_nall;
 
 static int isiin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int isiin_typef( fields *isiin, char *filename, int nref, param *p );
+static int isiin_typef( fields *isiin, const char *filename, int nref, param *p );
 static int isiin_convertf( fields *isiin, fields *info, int reftype, param *p );
-static int isiin_processf( fields *isiin, char *p, char *filename, long nref, param *pm );
+static int isiin_processf( fields *isiin, const char *p, const char *filename, long nref, param *pm );
 
 
 /*****************************************************
  PUBLIC: void isiin_initparams()
 *****************************************************/
-void
-isiin_initparams( param *p, const char *progname )
+int
+isiin_initparams( param *pm, const char *progname )
 {
-	p->readformat       = BIBL_ISIIN;
-	p->charsetin        = BIBL_CHARSET_DEFAULT;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 0;
-	p->xmlin            = 0;
-	p->utf8in           = 0;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->output_raw       = 0;
+	pm->readformat       = BIBL_ISIIN;
+	pm->charsetin        = BIBL_CHARSET_DEFAULT;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 0;
+	pm->xmlin            = 0;
+	pm->utf8in           = 0;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->output_raw       = 0;
 
-	p->readf    = isiin_readf;
-	p->processf = isiin_processf;
-	p->cleanf   = NULL;
-	p->typef    = isiin_typef;
-	p->convertf = isiin_convertf;
-	p->all      = isi_all;
-	p->nall     = isi_nall;
+	pm->readf    = isiin_readf;
+	pm->processf = isiin_processf;
+	pm->cleanf   = NULL;
+	pm->typef    = isiin_typef;
+	pm->convertf = isiin_convertf;
+	pm->all      = isi_all;
+	pm->nall     = isi_nall;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( !pm->progname ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 /*****************************************************
@@ -67,12 +73,12 @@
  *   char 1 = uppercase alphabetic character
  *   char 2 = uppercase alphabetic character or digit
  */
+
 static int
-is_isi_tag( char *buf )
+is_isi_tag( const char *buf )
 {
-	if ( ! (buf[0]>='A' && buf[0]<='Z') ) return 0;
-	if ( ! (((buf[1]>='A' && buf[1]<='Z'))||(buf[1]>='0'&&buf[1]<='9')))
-		return 0;
+	if ( !isupper( (unsigned char )buf[0] ) ) return 0;
+	if ( !( isupper( (unsigned char )buf[1] ) || isdigit( (unsigned char )buf[1] ) ) ) return 0;
 	return 1;
 }
 
@@ -135,8 +141,8 @@
  PUBLIC: int isiin_processf()
 *****************************************************/
 
-static char *
-process_tagged_line( str *tag, str *data, char *p )
+static const char *
+process_tagged_line( str *tag, str *data, const char *p )
 {
 	int i;
 
@@ -154,8 +160,8 @@
 	return p;
 }
 
-static char *
-process_untagged_line( str *data, char *p )
+static const char *
+process_untagged_line( str *data, const char *p )
 {
 	while ( *p==' ' || *p=='\t' ) p++;
 	while ( *p && *p!='\r' && *p!='\n' )
@@ -222,7 +228,7 @@
 }
 
 static int
-isiin_processf( fields *isiin, char *p, char *filename, long nref, param *pm )
+isiin_processf( fields *isiin, const char *p, const char *filename, long nref, param *pm )
 {
 	int status, tag_added = 0, ret = 1;
 	str tag, value;
@@ -264,7 +270,7 @@
  PUBLIC: int isiin_typef()
 *****************************************************/
 static int
-isiin_typef( fields *isiin, char *filename, int nref, param *p )
+isiin_typef( fields *isiin, const char *filename, int nref, param *p )
 {
 	int ntypename, nrefname, is_default;
 	char *refname = "", *typename="";
@@ -317,7 +323,7 @@
 isiin_keyword( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
 {
 	int fstatus, status = BIBL_OK;
-	char *p = invalue->data;
+	const char *p = invalue->data;
 	str keyword;
 
 	str_init( &keyword );
diff --git a/bibutils/isiout.c b/bibutils/isiout.c
--- a/bibutils/isiout.c
+++ b/bibutils/isiout.c
@@ -1,7 +1,7 @@
 /*
  * isiout.c
  *
- * Copyright (c) Chris Putnam 2008-2018
+ * Copyright (c) Chris Putnam 2008-2019
  *
  * Source code released under the GPL version 2
  *
@@ -9,43 +9,62 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
-#include "utf8.h"
-#include "str.h"
-#include "strsearch.h"
+#include "bibformats.h"
+#include "bibutils.h"
 #include "fields.h"
+#include "generic.h"
+#include "str.h"
 #include "title.h"
-#include "bibutils.h"
-#include "bibformats.h"
+#include "type.h"
+#include "utf8.h"
 
+/*****************************************************
+ PUBLIC: int isiout_initparams()
+*****************************************************/
+
 static int  isiout_write( fields *info, FILE *fp, param *p, unsigned long refnum );
-static void isiout_writeheader( FILE *outptr, param *p );
+static int  isiout_assemble( fields *in, fields *out, param *pm, unsigned long refnum );
 
-void
-isiout_initparams( param *p, const char *progname )
+int
+isiout_initparams( param *pm, const char *progname )
 {
-	p->writeformat      = BIBL_ISIOUT;
-	p->format_opts      = 0;
-	p->charsetout       = BIBL_CHARSET_DEFAULT;
-	p->charsetout_src   = BIBL_SRC_DEFAULT;
-	p->latexout         = 0;
-	p->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
-	p->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
-	p->xmlout           = BIBL_XMLOUT_FALSE;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->singlerefperfile = 0;
+	pm->writeformat      = BIBL_ISIOUT;
+	pm->format_opts      = 0;
+	pm->charsetout       = BIBL_CHARSET_DEFAULT;
+	pm->charsetout_src   = BIBL_SRC_DEFAULT;
+	pm->latexout         = 0;
+	pm->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
+	pm->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
+	pm->xmlout           = BIBL_XMLOUT_FALSE;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->singlerefperfile = 0;
 
-	if ( p->charsetout == BIBL_CHARSET_UNICODE ) {
-		p->utf8out = p->utf8bom = 1;
+	if ( pm->charsetout == BIBL_CHARSET_UNICODE ) {
+		pm->utf8out = pm->utf8bom = 1;
 	}
 
-	p->headerf = isiout_writeheader;
-	p->footerf = NULL;
-	p->writef  = isiout_write;
+	pm->headerf   = generic_writeheader;
+	pm->footerf   = NULL;
+	pm->assemblef = isiout_assemble;
+	pm->writef    = isiout_write;
+
+	if ( !pm->progname ) {
+		if ( !progname ) pm->progname = NULL;
+		else {
+			pm->progname = strdup( progname );
+			if ( !pm->progname ) return BIBL_ERR_MEMERR;
+		}
+	}
+
+	return BIBL_OK;
 }
 
+/*****************************************************
+ PUBLIC: int isiout_assemble()
+*****************************************************/
+
 enum {
         TYPE_UNKNOWN = 0,
         TYPE_ARTICLE = 1,
@@ -56,28 +75,31 @@
 static int 
 get_type( fields *in )
 {
-        int type = TYPE_UNKNOWN, i, n, level;
-	char *tag, *value;
-	n = fields_num( in );
-        for ( i=0; i<n; ++i ) {
-		tag = fields_tag( in, i, FIELDS_CHRP );
-                if ( strcasecmp( tag, "GENRE:MARC" ) &&
-		     strcasecmp( tag, "GENRE:BIBUTILS" ) &&
-		     strcasecmp( tag, "GENRE:UNKNOWN" ) ) continue;
-		value = fields_value( in, i, FIELDS_CHRP );
-		level = fields_level( in, i );
-                if ( !strcasecmp( value, "periodical" ) ||
-                     !strcasecmp( value, "academic journal" ) ||
-		     !strcasecmp( value, "journal article" ) ) {
-                        type = TYPE_ARTICLE;
-                } else if ( !strcasecmp( value, "book" ) ) {
-                        if ( level==0 ) type=TYPE_BOOK;
-                        else type=TYPE_INBOOK;
-		} else if ( !strcasecmp( value, "book chapter" ) ) {
-			type = TYPE_INBOOK;
-                }
-        }
-        return type;
+	match_type genre_matches[] = {
+		{ "periodical",         TYPE_ARTICLE,        LEVEL_ANY  },
+		{ "academic journal",   TYPE_ARTICLE,        LEVEL_ANY  },
+		{ "journal article",    TYPE_ARTICLE,        LEVEL_ANY  },
+		{ "book",               TYPE_BOOK,           LEVEL_MAIN },
+		{ "book",               TYPE_INBOOK,         LEVEL_ANY  },
+		{ "book chapter",       TYPE_INBOOK,         LEVEL_ANY  },
+		{ "collection",         TYPE_BOOK,           LEVEL_MAIN },
+		{ "collection",         TYPE_INBOOK,         LEVEL_ANY  },
+	};
+
+	int ngenre_matches = sizeof( genre_matches ) / sizeof( genre_matches[0] );
+
+	match_type issuance_matches[] = {
+		{ "monographic",        TYPE_BOOK,           LEVEL_MAIN },
+		{ "monographic",        TYPE_INBOOK,         LEVEL_ANY  },
+	};
+	int nissuance_matches = sizeof( issuance_matches ) / sizeof( issuance_matches[0] );
+
+	int type;
+
+	type = type_from_mods_hints( in, TYPE_FROM_GENRE, genre_matches, ngenre_matches, TYPE_UNKNOWN );
+	if ( type!=TYPE_UNKNOWN ) return type;
+
+	return type_from_mods_hints( in, TYPE_FROM_ISSUANCE, issuance_matches, nissuance_matches, TYPE_UNKNOWN );
 }
 
 static void
@@ -86,10 +108,12 @@
 	int fstatus;
 	char *s;
 
-	if ( type==TYPE_ARTICLE ) s = "Journal";
-	else if ( type==TYPE_INBOOK ) s = "Chapter";
-	else if ( type==TYPE_BOOK ) s = "Book";
-	else s = "Unknown";
+	switch( type ) {
+		case TYPE_ARTICLE: s = "Journal"; break;
+		case TYPE_INBOOK:  s = "Chapter"; break;
+		case TYPE_BOOK:    s = "Book";    break;
+		default:           s = "Unknown"; break;
+	}
 
 	fstatus = fields_add( out, "PT", s, LEVEL_MAIN );
 	if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
@@ -267,7 +291,7 @@
 }
 
 static int
-append_data( fields *in, fields *out )
+isiout_assemble( fields *in, fields *out, param *pm, unsigned long refnum )
 {
 	int type, status = BIBL_OK;
 
@@ -300,11 +324,15 @@
 	append_easy( in, "VOLUME",         "VL", LEVEL_ANY, out, &status );
 	append_easy( in, "ISSUE",          "IS", LEVEL_ANY, out, &status );
 	append_easy( in, "NUMBER",         "IS", LEVEL_ANY, out, &status );
+	append_easy( in, "PUBLISHER",      "PU", LEVEL_ANY, out, &status );
 	append_easy( in, "DOI",            "DI", LEVEL_ANY, out, &status );
+	append_easy( in, "URL",            "WP", LEVEL_ANY, out, &status );
 	append_easy( in, "ISIREFNUM",      "UT", LEVEL_ANY, out, &status );
 	append_easy( in, "LANGUAGE",       "LA", LEVEL_ANY, out, &status );
 	append_easy( in, "ISIDELIVERNUM",  "GA", LEVEL_ANY, out, &status );
 	append_keywords( in, out, &status );
+	append_easy( in, "ISBN",           "SN", LEVEL_ANY, out, &status );
+	append_easy( in, "ISSN",           "SN", LEVEL_ANY, out, &status );
 	append_easy( in, "ABSTRACT",       "AB", LEVEL_ANY, out, &status );
 	append_easy( in, "TIMESCITED",     "TC", LEVEL_ANY, out, &status );
 	append_easy( in, "NUMBERREFS",     "NR", LEVEL_ANY, out, &status );
@@ -314,27 +342,12 @@
 	return status;
 }
 
-static void
-output_verbose( fields *f, const char *type, unsigned long refnum )
-{
-	char *tag, *value;
-	int i, n, level;
-
-	fprintf( stderr, "REF #%lu %s---\n", refnum+1, type );
-
-	n = fields_num( f );
-	for ( i=0; i<n; ++i ) {
-		tag   = fields_tag( f, i, FIELDS_CHRP_NOUSE );
-		value = fields_value( f, i, FIELDS_CHRP_NOUSE );
-		level = fields_level( f, i );
-		fprintf( stderr, "\t'%s'\t'%s'\t%d\n", tag, value, level );
-	}
-
-	fflush( stderr );
-}
+/*****************************************************
+ PUBLIC: int isiout_write()
+*****************************************************/
 
-static void
-output( FILE *fp, fields *out )
+static int
+isiout_write( fields *out, FILE *fp, param *p, unsigned long refnum )
 {
 	int i;
 
@@ -346,33 +359,5 @@
 	}
         fprintf( fp, "ER\n\n" );
         fflush( fp );
-}
-
-static int
-isiout_write( fields *in, FILE *fp, param *p, unsigned long refnum )
-{
-	int status;
-	fields out;
-
-	fields_init( &out );
-
-	if ( p->format_opts & BIBL_FORMAT_VERBOSE )
-		output_verbose( in, "IN", refnum );
-
-	status = append_data( in, &out );
-
-	if ( status==BIBL_OK ) output( fp, &out );
-
-	if ( p->format_opts & BIBL_FORMAT_VERBOSE )
-		output_verbose( &out, "OUT", refnum );
-
-	fields_free( &out );
-
-	return status;
-}
-
-static void
-isiout_writeheader( FILE *outptr, param *p )
-{
-	if ( p->utf8bom ) utf8_writebom( outptr );
+	return BIBL_OK;
 }
diff --git a/bibutils/isitypes.c b/bibutils/isitypes.c
--- a/bibutils/isitypes.c
+++ b/bibutils/isitypes.c
@@ -1,7 +1,7 @@
 /*
  * isitypes.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -22,7 +22,7 @@
 	{ "PU",     "PUBLISHER",             SIMPLE,   LEVEL_HOST },
 	{ "PI",     "ADDRESS",               SIMPLE,   LEVEL_HOST }, /* publisher city */
 	{ "C1",     "ADDRESS",               SIMPLE,   LEVEL_MAIN }, /* author address */
-	{ "PA",     "ADDRESS",               SIMPLE,   LEVEL_HOST }, /* publisher address */
+	{ "PA",     "ADDRESS:PUBLISHER",     SIMPLE,   LEVEL_HOST }, /* publisher address */
 	{ "RP",     "REPRINTADDRESS",        SIMPLE,   LEVEL_MAIN },
 	{ "PY",     "PARTDATE:YEAR",         SIMPLE,   LEVEL_MAIN },
 	{ "PD",     "PARTDATE:MONTH",        SIMPLE,   LEVEL_MAIN },
@@ -64,7 +64,7 @@
 	{ "PU",     "PUBLISHER",             SIMPLE,   LEVEL_MAIN },
 	{ "PI",     "ADDRESS",               SIMPLE,   LEVEL_MAIN }, /* publisher city */
 	{ "C1",     "ADDRESS",               SIMPLE,   LEVEL_MAIN }, /* author address */
-	{ "PA",     "ADDRESS",               SIMPLE,   LEVEL_MAIN }, /* publisher address */
+	{ "PA",     "ADDRESS:PUBLISHER",     SIMPLE,   LEVEL_MAIN }, /* publisher address */
 	{ "RP",     "REPRINTADDRESS",        SIMPLE,   LEVEL_MAIN },
 	{ "PY",     "DATE:YEAR",             SIMPLE,   LEVEL_MAIN },
 	{ "PD",     "DATE:MONTH",            SIMPLE,   LEVEL_MAIN },
@@ -104,7 +104,7 @@
 	{ "PU",     "PUBLISHER",             SIMPLE,   LEVEL_HOST },
 	{ "PI",     "ADDRESS",               SIMPLE,   LEVEL_HOST }, /* publisher city */
 	{ "C1",     "ADDRESS",               SIMPLE,   LEVEL_MAIN }, /* author address */
-	{ "PA",     "ADDRESS",               SIMPLE,   LEVEL_HOST }, /* publisher address */
+	{ "PA",     "ADDRESS:PUBLISHER",     SIMPLE,   LEVEL_HOST }, /* publisher address */
 	{ "RP",     "REPRINTADDRESS",        SIMPLE,   LEVEL_MAIN },
 	{ "PY",     "PARTDATE:YEAR",         SIMPLE,   LEVEL_MAIN },
 	{ "PD",     "PARTDATE:MONTH",        SIMPLE,   LEVEL_MAIN },
@@ -146,7 +146,7 @@
 	{ "PU",     "PUBLISHER",             SIMPLE,   LEVEL_HOST },
 	{ "PI",     "ADDRESS",               SIMPLE,   LEVEL_HOST }, /* publisher city */
 	{ "C1",     "ADDRESS",               SIMPLE,   LEVEL_MAIN }, /* author address */
-	{ "PA",     "ADDRESS",               SIMPLE,   LEVEL_HOST }, /* publisher address */
+	{ "PA",     "ADDRESS:PUBLISHER",     SIMPLE,   LEVEL_HOST }, /* publisher address */
 	{ "RP",     "REPRINTADDRESS",        SIMPLE,   LEVEL_MAIN },
 	{ "PY",     "PARTDATE:YEAR",         SIMPLE,   LEVEL_MAIN },
 	{ "PD",     "PARTDATE:MONTH",        SIMPLE,   LEVEL_MAIN },
diff --git a/bibutils/latex.c b/bibutils/latex.c
--- a/bibutils/latex.c
+++ b/bibutils/latex.c
@@ -3,7 +3,7 @@
  *
  * convert between latex special chars and unicode
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/latex.h b/bibutils/latex.h
--- a/bibutils/latex.h
+++ b/bibutils/latex.h
@@ -1,7 +1,7 @@
 /*
  * latex.h
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/marc_auth.c b/bibutils/marc_auth.c
--- a/bibutils/marc_auth.c
+++ b/bibutils/marc_auth.c
@@ -1,7 +1,7 @@
 /*
  * marc_auth.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/marc_auth.h b/bibutils/marc_auth.h
--- a/bibutils/marc_auth.h
+++ b/bibutils/marc_auth.h
@@ -3,7 +3,7 @@
  *
  * Recognize the MARC authority vocabulary for genre and resource.
  *
- * Copyright (c) Chris Putnam 2008-2018
+ * Copyright (c) Chris Putnam 2008-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/medin.c b/bibutils/medin.c
--- a/bibutils/medin.c
+++ b/bibutils/medin.c
@@ -1,7 +1,7 @@
 /*
  * medin.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
@@ -19,40 +19,45 @@
 #include "bibformats.h"
 
 static int medin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int medin_processf( fields *medin, char *data, char *filename, long nref, param *p );
+static int medin_processf( fields *medin, const char *data, const char *filename, long nref, param *p );
 
 
 /*****************************************************
  PUBLIC: void medin_initparams()
 *****************************************************/
-void
-medin_initparams( param *p, const char *progname )
+int
+medin_initparams( param *pm, const char *progname )
 {
-	p->readformat       = BIBL_MEDLINEIN;
-	p->charsetin        = BIBL_CHARSET_UNICODE;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 0;
-	p->xmlin            = 1;
-	p->utf8in           = 1;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->output_raw       = BIBL_RAW_WITHMAKEREFID |
+	pm->readformat       = BIBL_MEDLINEIN;
+	pm->charsetin        = BIBL_CHARSET_UNICODE;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 0;
+	pm->xmlin            = 1;
+	pm->utf8in           = 1;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->output_raw       = BIBL_RAW_WITHMAKEREFID |
 	                      BIBL_RAW_WITHCHARCONVERT;
 
-	p->readf    = medin_readf;
-	p->processf = medin_processf;
-	p->cleanf   = NULL;
-	p->typef    = NULL;
-	p->convertf = NULL;
-	p->all      = NULL;
-	p->nall     = 0;
+	pm->readf    = medin_readf;
+	pm->processf = medin_processf;
+	pm->cleanf   = NULL;
+	pm->typef    = NULL;
+	pm->convertf = NULL;
+	pm->all      = NULL;
+	pm->nall     = 0;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( !pm->progname ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 /*****************************************************
@@ -172,7 +177,7 @@
 
 /*            <MedlineDate>2003 Jan-Feb</MedlineDate> */
 static int
-medin_medlinedate( fields *info, char *p, int level )
+medin_medlinedate( fields *info, const char *p, int level )
 {
 	int fstatus;
 	str tmp;
@@ -303,7 +308,7 @@
 {
 	int i, fstatus, status;
 	str sp, ep;
-	char *p, *pp;
+	const char *p, *pp;
 	if ( xml_tag_matches( node, "MedlinePgn" ) && node->value.len ) {
 		strs_init( &sp, &ep, NULL );
 		p = str_cpytodelim( &sp, xml_value_cstr( node ), "-", 1 );
@@ -522,7 +527,7 @@
 		if ( status!=BIBL_OK ) return status;
 	}
 	if ( node->down ) {
-		medin_pubmeddata( node->down, info );
+		status = medin_pubmeddata( node->down, info );
 		if ( status!=BIBL_OK ) return status;
 	}
 	return BIBL_OK;
@@ -625,7 +630,7 @@
 }
 
 static int
-medin_processf( fields *medin, char *data, char *filename, long nref, param *p )
+medin_processf( fields *medin, const char *data, const char *filename, long nref, param *p )
 {
 	int status;
 	xml top;
diff --git a/bibutils/modsin.c b/bibutils/modsin.c
--- a/bibutils/modsin.c
+++ b/bibutils/modsin.c
@@ -1,7 +1,7 @@
 /*
  * modsin.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
@@ -29,42 +29,47 @@
 #include "bibformats.h"
 
 static int modsin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int modsin_processf( fields *medin, char *data, char *filename, long nref, param *p );
+static int modsin_processf( fields *medin, const char *data, const char *filename, long nref, param *p );
 
 /*****************************************************
  PUBLIC: void modsin_initparams()
 *****************************************************/
-void
-modsin_initparams( param *p, const char *progname )
+int
+modsin_initparams( param *pm, const char *progname )
 {
 
-	p->readformat       = BIBL_MODSIN;
-	p->format_opts      = 0;
-	p->charsetin        = BIBL_CHARSET_UNICODE;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 0;
-	p->utf8in           = 1;
-	p->xmlin            = 1;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->singlerefperfile = 0;
-	p->output_raw       = BIBL_RAW_WITHMAKEREFID |
+	pm->readformat       = BIBL_MODSIN;
+	pm->format_opts      = 0;
+	pm->charsetin        = BIBL_CHARSET_UNICODE;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 0;
+	pm->utf8in           = 1;
+	pm->xmlin            = 1;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->singlerefperfile = 0;
+	pm->output_raw       = BIBL_RAW_WITHMAKEREFID |
 	                      BIBL_RAW_WITHCHARCONVERT;
 
-	p->readf    = modsin_readf;
-	p->processf = modsin_processf;
-	p->cleanf   = NULL;
-	p->typef    = NULL;
-	p->convertf = NULL;
-	p->all      = NULL;
-	p->nall     = 0;
+	pm->readf    = modsin_readf;
+	pm->processf = modsin_processf;
+	pm->cleanf   = NULL;
+	pm->typef    = NULL;
+	pm->convertf = NULL;
+	pm->all      = NULL;
+	pm->nall     = 0;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( !pm->progname ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 /*****************************************************
@@ -122,7 +127,7 @@
 modsin_date( xml *node, fields *info, int level, int part )
 {
 	int fstatus, status = BIBL_OK;
-	char *tag, *p;
+	const char *tag, *p;
 	str s;
 
 	str_init( &s );
@@ -1063,7 +1068,7 @@
 }
 
 static int
-modsin_processf( fields *modsin, char *data, char *filename, long nref, param *p )
+modsin_processf( fields *modsin, const char *data, const char *filename, long nref, param *p )
 {
 	int status;
 	xml top;
diff --git a/bibutils/modsout.c b/bibutils/modsout.c
--- a/bibutils/modsout.c
+++ b/bibutils/modsout.c
@@ -1,7 +1,7 @@
 /*
  * modsout.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
@@ -22,31 +22,50 @@
 #include "marc_auth.h"
 #include "bibformats.h"
 
+/*****************************************************
+ PUBLIC: int modsout_initparams()
+*****************************************************/
+
 static void modsout_writeheader( FILE *outptr, param *p );
 static void modsout_writefooter( FILE *outptr );
 static int  modsout_write( fields *info, FILE *outptr, param *p, unsigned long numrefs );
 
-void
-modsout_initparams( param *p, const char *progname )
+int
+modsout_initparams( param *pm, const char *progname )
 {
-	p->writeformat      = BIBL_MODSOUT;
-	p->format_opts      = 0;
-	p->charsetout       = BIBL_CHARSET_UNICODE;
-	p->charsetout_src   = BIBL_SRC_DEFAULT;
-	p->latexout         = 0;
-	p->utf8out          = 1;
-	p->utf8bom          = 1;
-	p->xmlout           = BIBL_XMLOUT_TRUE;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->singlerefperfile = 0;
+	pm->writeformat      = BIBL_MODSOUT;
+	pm->format_opts      = 0;
+	pm->charsetout       = BIBL_CHARSET_UNICODE;
+	pm->charsetout_src   = BIBL_SRC_DEFAULT;
+	pm->latexout         = 0;
+	pm->utf8out          = 1;
+	pm->utf8bom          = 1;
+	pm->xmlout           = BIBL_XMLOUT_TRUE;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->singlerefperfile = 0;
 
-	p->headerf = modsout_writeheader;
-	p->footerf = modsout_writefooter;
-	p->writef  = modsout_write;
+	pm->headerf   = modsout_writeheader;
+	pm->footerf   = modsout_writefooter;
+	pm->assemblef = NULL;
+	pm->writef    = modsout_write;
+
+	if ( !pm->progname ) {
+		if ( !progname ) pm->progname = NULL;
+		else {
+			pm->progname = strdup( progname );
+			if ( !pm->progname ) return BIBL_ERR_MEMERR;
+		}
+	}
+
+	return BIBL_OK;
 }
 
+/*****************************************************
+ PUBLIC: int modsout_write()
+*****************************************************/
+
 /* output_tag()
  *
  * mode = TAG_OPEN,         "<tag>"
@@ -446,12 +465,13 @@
 output_origin( fields *f, FILE *outptr, int level )
 {
 	convert parts[] = {
-		{ "issuance",	  "ISSUANCE",      0, 0 },
-		{ "publisher",	  "PUBLISHER",     0, 0 },
-		{ "place",	  "ADDRESS",       0, 1 },
-		{ "place",	  "AUTHORADDRESS", 0, 0 },
-		{ "edition",	  "EDITION",       0, 0 },
-		{ "dateCaptured", "URLDATE",       0, 0 }
+		{ "issuance",	  "ISSUANCE",          0, 0 },
+		{ "publisher",	  "PUBLISHER",         0, 0 },
+		{ "place",	  "ADDRESS",           0, 1 },
+		{ "place",        "ADDRESS:PUBLISHER", 0, 0 },
+		{ "place",	  "ADDRESS:AUTHOR",    0, 0 },
+		{ "edition",	  "EDITION",           0, 0 },
+		{ "dateCaptured", "URLDATE",           0, 0 }
 	};
 	int nparts = sizeof( parts ) / sizeof( parts[0] );
 	int i, found, datefound, datepos[ NUM_DATE_TYPES ];
@@ -905,17 +925,19 @@
 		{ "isrn",      "ISRN",      0, 0 },
 	};
 	int ntypes = sizeof( sn_types ) / sizeof( sn_types[0] );
-	int i, n;
+	int i, n, found;
 
 	/* output call number */
 	n = fields_find( f, "CALLNUMBER", level );
 	output_fil( outptr, lvl2indent(level), "classification", f, n, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 
 	/* output specialized serialnumber */
-	convert_findallfields( f, sn_types, ntypes, level );
-	for ( i=0; i<ntypes; ++i ) {
-		if ( sn_types[i].pos==-1 ) continue;
-		output_fil( outptr, lvl2indent(level), "identifier", f, sn_types[i].pos, TAG_OPENCLOSE, TAG_NEWLINE, "type", sn_types[i].mods, NULL );
+	found = convert_findallfields( f, sn_types, ntypes, level );
+	if ( found ) {
+		for ( i=0; i<ntypes; ++i ) {
+			if ( sn_types[i].pos==-1 ) continue;
+			output_fil( outptr, lvl2indent(level), "identifier", f, sn_types[i].pos, TAG_OPENCLOSE, TAG_NEWLINE, "type", sn_types[i].mods, NULL );
+		}
 	}
 
 	/* output _all_ elements of type SERIALNUMBER */
@@ -1128,6 +1150,10 @@
 	return BIBL_OK;
 }
 
+/*****************************************************
+ PUBLIC: int modsout_writeheader()
+*****************************************************/
+
 static void
 modsout_writeheader( FILE *outptr, param *p )
 {
@@ -1136,6 +1162,10 @@
 			charset_get_xmlname( p->charsetout ) );
 	fprintf(outptr,"<modsCollection xmlns=\"http://www.loc.gov/mods/v3\">\n");
 }
+
+/*****************************************************
+ PUBLIC: int modsout_writefooter()
+*****************************************************/
 
 static void
 modsout_writefooter( FILE *outptr )
diff --git a/bibutils/modstypes.c b/bibutils/modstypes.c
--- a/bibutils/modstypes.c
+++ b/bibutils/modstypes.c
@@ -1,7 +1,7 @@
 /*
  * modstypes.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *      
diff --git a/bibutils/modstypes.h b/bibutils/modstypes.h
--- a/bibutils/modstypes.h
+++ b/bibutils/modstypes.h
@@ -1,7 +1,7 @@
 /*
  * modstypes.h
  *
- * Copyright (c) Chris Putnam 2008-2018
+ * Copyright (c) Chris Putnam 2008-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/name.c b/bibutils/name.c
--- a/bibutils/name.c
+++ b/bibutils/name.c
@@ -3,7 +3,7 @@
  *
  * mangle names w/ and w/o commas
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
@@ -26,10 +26,10 @@
  * to 'family suffix, given given
  */
 void
-name_build_withcomma( str *s, char *p )
+name_build_withcomma( str *s, const char *p )
 {
+	const char *suffix, *stopat;
 	int nseps = 0, nch;
-	char *suffix, *stopat;
 
 	str_empty( s );
 
@@ -361,7 +361,7 @@
 }
 
 int
-name_addmultielement( fields *info, char *tag, slist *tokens, int begin, int end, int level )
+name_addmultielement( fields *info, const char *tag, slist *tokens, int begin, int end, int level )
 {
 	int status, ok = 1;
 	str name;
@@ -385,16 +385,20 @@
  * is set).
  */
 int
-name_addsingleelement( fields *info, char *tag, char *name, int level, int corp )
+name_addsingleelement( fields *info, const char *tag, const char *name, int level, int corp )
 {
 	int status, ok = 1;
 	str outtag;
+
 	str_init( &outtag );
+
 	str_strcpyc( &outtag, tag );
 	if ( !corp ) str_strcatc( &outtag, ":ASIS" );
 	else str_strcatc( &outtag, ":CORP" );
+
 	status = fields_add_can_dup( info, outtag.data, name, level );
 	if ( status!=FIELDS_OK ) ok = 0;
+
 	str_free( &outtag );
 	return ok;
 }
@@ -452,10 +456,10 @@
 	return ret;
 }
 
-static char *
-name_copy( str *name, char *p )
+static const char *
+name_copy( str *name, const char *p )
 {
-	char *start, *end, *q;
+	const char *start, *end, *q;
 
 	str_empty( name );
 
@@ -491,7 +495,7 @@
  * "Author, H. F."
  */
 int
-name_add( fields *info, char *tag, char *q, int level, slist *asis, slist *corps )
+name_add( fields *info, const char *tag, const char *q, int level, slist *asis, slist *corps )
 {
 	int ok, status, nametype, ret = 1;
 	str inname, outname;
diff --git a/bibutils/name.h b/bibutils/name.h
--- a/bibutils/name.h
+++ b/bibutils/name.h
@@ -3,7 +3,7 @@
  *
  * mangle names w/ and w/o commas
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
@@ -15,12 +15,12 @@
 #include "slist.h"
 #include "fields.h"
 
-extern int  name_add( fields *info, char *tag, char *q, int level, slist *asis, slist *corps );
-extern void name_build_withcomma( str *s, char *p );
-extern int  name_parse( str *outname, str *inname, slist *asis, slist *corps );
-extern int  name_addsingleelement( fields *info, char *tag, char *name, int level, int corp );
-extern int  name_addmultielement( fields *info, char *tag, slist *tokens, int begin, int end, int level );
-extern int  name_findetal( slist *tokens );
+int  name_add( fields *info, const char *tag, const char *q, int level, slist *asis, slist *corps );
+void name_build_withcomma( str *s, const char *p );
+int  name_parse( str *outname, str *inname, slist *asis, slist *corps );
+int  name_addsingleelement( fields *info, const char *tag, const char *name, int level, int corp );
+int  name_addmultielement( fields *info, const char *tag, slist *tokens, int begin, int end, int level );
+int  name_findetal( slist *tokens );
 
 #endif
 
diff --git a/bibutils/nbibin.c b/bibutils/nbibin.c
--- a/bibutils/nbibin.c
+++ b/bibutils/nbibin.c
@@ -1,7 +1,7 @@
 /*
  * nbibin.c
  *
- * Copyright (c) Chris Putnam 2016-2018
+ * Copyright (c) Chris Putnam 2016-2019
  *
  * Source code released under the GPL version 2
  *
@@ -30,37 +30,42 @@
 *****************************************************/
 
 static int nbib_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int nbib_processf( fields *nbib, char *p, char *filename, long nref, param *pm );
-static int nbib_typef( fields *nbib, char *filename, int nref, param *p );
+static int nbib_processf( fields *nbib, const char *p, const char *filename, long nref, param *pm );
+static int nbib_typef( fields *nbib, const char *filename, int nref, param *p );
 static int nbib_convertf( fields *nbib, fields *info, int reftype, param *p );
 
-void
-nbibin_initparams( param *p, const char *progname )
+int
+nbibin_initparams( param *pm, const char *progname )
 {
-	p->readformat       = BIBL_NBIBIN;
-	p->charsetin        = BIBL_CHARSET_DEFAULT;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 0;
-	p->xmlin            = 0;
-	p->utf8in           = 0;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->output_raw       = 0;
+	pm->readformat       = BIBL_NBIBIN;
+	pm->charsetin        = BIBL_CHARSET_DEFAULT;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 0;
+	pm->xmlin            = 0;
+	pm->utf8in           = 0;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->output_raw       = 0;
 
-	p->readf    = nbib_readf;
-	p->processf = nbib_processf;
-	p->cleanf   = NULL;
-	p->typef    = nbib_typef;
-	p->convertf = nbib_convertf;
-	p->all      = nbib_all;
-	p->nall     = nbib_nall;
+	pm->readf    = nbib_readf;
+	pm->processf = nbib_processf;
+	pm->cleanf   = NULL;
+	pm->typef    = nbib_typef;
+	pm->convertf = nbib_convertf;
+	pm->all      = nbib_all;
+	pm->nall     = nbib_nall;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( !pm->progname ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 /*****************************************************
@@ -75,15 +80,15 @@
     character 5 = dash (ansi 45)
     character 6 = space (ansi 32)
 */
-static int
-is_upperchar( char c )
+static inline int
+is_upperchar( const char c )
 {
 	if ( c>='A' && c<='Z' ) return 1;
 	else return 0;
 }
 
-static int
-is_upperchar_space( char c )
+static inline int
+is_upperchar_space( const char c )
 {
 	if ( c==' ' ) return 1;
 	if ( c>='A' && c<='Z' ) return 1;
@@ -91,7 +96,7 @@
 }
 
 static int
-nbib_istag( char *buf )
+nbib_istag( const char *buf )
 {
 	if ( !is_upperchar( buf[0] ) ) return 0;
 	if ( !is_upperchar( buf[1] ) ) return 0;
@@ -136,7 +141,7 @@
 	while ( !haveref && readmore( fp, buf, bufsize, bufpos, line ) ) {
 
 		/* ...references are terminated by an empty line */
-		if ( !line->data || line->len==0 ) {
+		if ( str_is_empty( line ) ) {
 			if ( reference->len ) haveref = 1;
 			continue;
 		}
@@ -181,18 +186,18 @@
  PUBLIC: int nbib_processf()
 *****************************************************/
 
-static char*
-process_line2( str *tag, str *data, char *p )
+static const char*
+process_line2( str *tag, str *value, const char *p )
 {
 	while ( *p==' ' || *p=='\t' ) p++;
 	while ( *p && *p!='\r' && *p!='\n' )
-		str_addchar( data, *p++ );
+		str_addchar( value, *p++ );
 	while ( *p=='\r' || *p=='\n' ) p++;
 	return p;
 }
 
-static char*
-process_line( str *tag, str *data, char *p )
+static const char*
+process_line( str *tag, str *value, const char *p )
 {
 	int i;
 
@@ -204,41 +209,41 @@
 	}
 	while ( *p==' ' || *p=='\t' ) p++;
 	while ( *p && *p!='\r' && *p!='\n' )
-		str_addchar( data, *p++ );
-	str_trimendingws( data );
+		str_addchar( value, *p++ );
+	str_trimendingws( value );
 	while ( *p=='\n' || *p=='\r' ) p++;
 	return p;
 }
 
 static int
-nbib_processf( fields *nbib, char *p, char *filename, long nref, param *pm )
+nbib_processf( fields *nbib, const char *p, const char *filename, long nref, param *pm )
 {
-	str tag, data;
+	str tag, value;
 	int status, n;
 
-	strs_init( &tag, &data, NULL );
+	strs_init( &tag, &value, NULL );
 
 	while ( *p ) {
 		if ( nbib_istag( p ) )
-			p = process_line( &tag, &data, p );
+			p = process_line( &tag, &value, p );
 		/* no anonymous fields allowed */
 		if ( str_has_value( &tag ) ) {
-			status = fields_add( nbib, str_cstr( &tag ), str_cstr( &data ), 0 );
+			status = fields_add( nbib, str_cstr( &tag ), str_cstr( &value ), 0 );
 			if ( status!=FIELDS_OK ) return 0;
 		} else {
-			p = process_line2( &tag, &data, p );
+			p = process_line2( &tag, &value, p );
 			n = fields_num( nbib );
-			if ( data.len && n>0 ) {
+			if ( value.len && n>0 ) {
 				str *od;
 				od = fields_value( nbib, n-1, FIELDS_STRP );
 				str_addchar( od, ' ' );
-				str_strcat( od, &data );
+				str_strcat( od, &value );
 			}
 		}
-		strs_empty( &tag, &data, NULL );
+		strs_empty( &tag, &value, NULL );
 	}
 
-	strs_free( &tag, &data, NULL );
+	strs_free( &tag, &value, NULL );
 	return 1;
 }
 
@@ -253,9 +258,9 @@
  * PT  - Review
  */
 static int
-nbib_typef( fields *nbib, char *filename, int nref, param *p )
+nbib_typef( fields *nbib, const char *filename, int nref, param *p )
 {
-	int reftype, nrefname, is_default;
+	int reftype = 0, nrefname, is_default;
 	char *typename, *refname = "";
 	vplist_index i;
 	vplist a;
@@ -489,8 +494,8 @@
 
 	for ( i=0; i<nfields; ++i ) {
 		intag = fields_tag( bibin, i, FIELDS_STRP );
-		if ( !translate_oldtag( intag->data, reftype, p->all, p->nall, &process, &level, &outtag ) ) {
-			nbib_report_notag( p, intag->data );
+		if ( !translate_oldtag( str_cstr( intag ), reftype, p->all, p->nall, &process, &level, &outtag ) ) {
+			nbib_report_notag( p, str_cstr( intag ) );
 			continue;
 		}
 		invalue = fields_value( bibin, i, FIELDS_STRP );
diff --git a/bibutils/nbibout.c b/bibutils/nbibout.c
--- a/bibutils/nbibout.c
+++ b/bibutils/nbibout.c
@@ -1,7 +1,7 @@
 /*
  * nbibout.c
  *
- * Copyright (c) Chris Putnam 2018
+ * Copyright (c) Chris Putnam 2018-2019
  *
  * Source code released under the GPL version 2
  *
@@ -13,41 +13,58 @@
 #include "utf8.h"
 #include "str.h"
 #include "is_ws.h"
-#include "strsearch.h"
 #include "fields.h"
+#include "generic.h"
 #include "iso639_3.h"
 #include "title.h"
 #include "bibutils.h"
 #include "bibformats.h"
 
+/*****************************************************
+ PUBLIC: int nbibout_initparams()
+*****************************************************/
+
 static int  nbibout_write( fields *info, FILE *fp, param *p, unsigned long refnum );
-static void nbibout_writeheader( FILE *outptr, param *p );
 
-void
-nbibout_initparams( param *p, const char *progname )
+int
+nbibout_initparams( param *pm, const char *progname )
 {
-	p->writeformat      = BIBL_NBIBOUT;
-	p->format_opts      = 0;
-	p->charsetout       = BIBL_CHARSET_DEFAULT;
-	p->charsetout_src   = BIBL_SRC_DEFAULT;
-	p->latexout         = 0;
-	p->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
-	p->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
-	p->xmlout           = BIBL_XMLOUT_FALSE;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->singlerefperfile = 0;
+	pm->writeformat      = BIBL_NBIBOUT;
+	pm->format_opts      = 0;
+	pm->charsetout       = BIBL_CHARSET_DEFAULT;
+	pm->charsetout_src   = BIBL_SRC_DEFAULT;
+	pm->latexout         = 0;
+	pm->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
+	pm->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
+	pm->xmlout           = BIBL_XMLOUT_FALSE;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->singlerefperfile = 0;
 
-	if ( p->charsetout == BIBL_CHARSET_UNICODE ) {
-		p->utf8out = p->utf8bom = 1;
+	if ( pm->charsetout == BIBL_CHARSET_UNICODE ) {
+		pm->utf8out = pm->utf8bom = 1;
 	}
 
-	p->headerf = nbibout_writeheader;
-	p->footerf = NULL;
-	p->writef  = nbibout_write;
+	pm->headerf = generic_writeheader;
+	pm->footerf = NULL;
+	pm->writef  = nbibout_write;
+
+	if ( !pm->progname ) {
+		if ( !progname ) pm->progname = NULL;
+		else {
+			pm->progname = strdup( progname );
+			if ( !pm->progname ) return BIBL_ERR_MEMERR;
+		}
+	}
+
+	return BIBL_OK;
 }
 
+/*****************************************************
+ PUBLIC: int nbibout_write()
+*****************************************************/
+
 enum {
         TYPE_UNKNOWN = 0,
         TYPE_ARTICLE = 1,
@@ -535,10 +552,4 @@
 	fields_free( &out );
 
 	return status;
-}
-
-static void
-nbibout_writeheader( FILE *outptr, param *p )
-{
-	if ( p->utf8bom ) utf8_writebom( outptr );
 }
diff --git a/bibutils/nbibtypes.c b/bibutils/nbibtypes.c
--- a/bibutils/nbibtypes.c
+++ b/bibutils/nbibtypes.c
@@ -1,7 +1,7 @@
 /*
  * nbibtypes.c
  *
- * Copyright (c) Chris Putnam 2016-2018
+ * Copyright (c) Chris Putnam 2016-2019
  *
  * Program and source code released under the GPL version 2
  *
diff --git a/bibutils/notes.c b/bibutils/notes.c
--- a/bibutils/notes.c
+++ b/bibutils/notes.c
@@ -1,7 +1,7 @@
 /*
  * notes.c
  *
- * Copyright (c) Chris Putnam 2016-2018
+ * Copyright (c) Chris Putnam 2016-2019
  *
  * Program and source code released under the GPL version 2
  *
@@ -42,7 +42,7 @@
 	};
 	int nprefixes = sizeof( prefixes ) / sizeof( prefixes[0] );
 
-	char *p = invalue->data;
+	const char *p = str_cstr( invalue );
 	char *tag = "URL";
 	int fstatus;
 	int i;
@@ -70,7 +70,7 @@
 {
 	int doi, fstatus;
 
-	doi = is_doi( invalue->data );
+	doi = is_doi( str_cstr( invalue ) );
 
 	if ( doi != -1 ) {
 		fstatus = fields_add( bibout, "DOI", &(invalue->data[doi]), level );
@@ -86,8 +86,8 @@
 {
 	int fstatus, done = 0, ok = 1;
 
-	if ( !is_embedded_link( invalue->data ) ) {
-		fstatus = fields_add( bibout, "NOTES", invalue->data, level );
+	if ( !is_embedded_link( str_cstr( invalue ) ) ) {
+		fstatus = fields_add( bibout, "NOTES", str_cstr( invalue ), level );
 		if ( fstatus != FIELDS_OK ) ok = 0;
 	}
 
diff --git a/bibutils/notes.h b/bibutils/notes.h
--- a/bibutils/notes.h
+++ b/bibutils/notes.h
@@ -1,7 +1,7 @@
 /*
  * notes.h
  *
- * Copyright (c) Chris Putnam 2016-2018
+ * Copyright (c) Chris Putnam 2016-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/pages.c b/bibutils/pages.c
--- a/bibutils/pages.c
+++ b/bibutils/pages.c
@@ -1,7 +1,7 @@
 /*
  * pages.c
  *
- * Copyright (c) Chris Putnam 2016-2018
+ * Copyright (c) Chris Putnam 2016-2019
  *
  * Program and source code released under GPL verison 2
  */
@@ -27,14 +27,14 @@
 {
 	/* -30 is the first character of a UTF8 em-dash and en-dash */
 	const char terminators[] = { ' ', '-', '\t', '\r', '\n', -30, '\0' };
-	char *p;
+	const char *p;
 
 	str_empty( begin );
 	str_empty( end );
 
 	if ( input->len==0 ) return;
 
-	p = skip_ws( input->data );
+	p = skip_ws( str_cstr( input ) );
 	while ( *p && !strchr( terminators, *p ) )
 		str_addchar( begin, *p++ );
 
@@ -67,7 +67,7 @@
 	}
 
 	if ( start.len>0 ) {
-		fstatus = fields_add( bibout, "PAGES:START", start.data, level );
+		fstatus = fields_add( bibout, "PAGES:START", str_cstr( &start ), level );
 		if ( fstatus!=FIELDS_OK ) {
 			status = 0;
 			goto out;
@@ -75,7 +75,7 @@
 	}
 
 	if ( stop.len>0 ) {
-		fstatus = fields_add( bibout, "PAGES:STOP", stop.data, level );
+		fstatus = fields_add( bibout, "PAGES:STOP", str_cstr( &stop ), level );
 		if ( fstatus!=FIELDS_OK ) status = 0;
 	}
 
diff --git a/bibutils/pages.h b/bibutils/pages.h
--- a/bibutils/pages.h
+++ b/bibutils/pages.h
@@ -1,7 +1,7 @@
 /*
  * pages.h
  *
- * Copyright (c) Chris Putnam 2016-2018
+ * Copyright (c) Chris Putnam 2016-2019
  *
  * Program and source code released under the GPL version 2
  */
diff --git a/bibutils/reftypes.c b/bibutils/reftypes.c
--- a/bibutils/reftypes.c
+++ b/bibutils/reftypes.c
@@ -1,7 +1,7 @@
 /*
  * reftypes.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
@@ -13,7 +13,7 @@
 #include "reftypes.h"
 
 int
-get_reftype( char *p, long refnum, char *progname, variants *all, int nall, char *tag, int *is_default, int chattiness )
+get_reftype( const char *p, long refnum, char *progname, variants *all, int nall, char *tag, int *is_default, int chattiness )
 {
 	int i;
 
@@ -38,26 +38,27 @@
 }
 
 int
-process_findoldtag( char *oldtag, int reftype, variants all[], int nall )
+process_findoldtag( const char *oldtag, int reftype, variants all[], int nall )
 {
         variants *v;
         int i;
+
         v = &(all[reftype]);
-/*      for ( i=0; i<(all[reftype]).ntags; ++i )*/
-        for ( i=0; i<v->ntags; ++i )
-/*              if ( !strcasecmp( ((all[reftype]).tags[i]).oldstr, oldtag ) )*/
+        for ( i=0; i<v->ntags; ++i ) {
                 if ( !strcasecmp( (v->tags[i]).oldstr, oldtag ) )
                         return i;
+	}
         return -1;
 }
 
 /* translate_oldtag()
  */
 int
-translate_oldtag( char *oldtag, int reftype, variants all[], int nall,
+translate_oldtag( const char *oldtag, int reftype, variants all[], int nall,
 		int *processingtype, int *level, char **newtag )
 {
 	int n;
+
 	n = process_findoldtag( oldtag, reftype, all, nall );
 	if ( n!=-1 ) {
 		*processingtype = ((all[reftype]).tags[n]).processingtype;
@@ -65,5 +66,6 @@
 		*newtag         = ((all[reftype]).tags[n]).newstr;
 		return 1;
 	}
+
 	return 0;
 }
diff --git a/bibutils/reftypes.h b/bibutils/reftypes.h
--- a/bibutils/reftypes.h
+++ b/bibutils/reftypes.h
@@ -1,7 +1,7 @@
 /*
  * reftypes.h
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
@@ -56,8 +56,8 @@
 	int     ntags;
 } variants;
 
-int get_reftype( char *q, long refnum, char *progname, variants *all, int nall, char *tag, int *is_default, int chattiness );
-int process_findoldtag( char *oldtag, int reftype, variants all[], int nall );
-int translate_oldtag( char *oldtag, int reftype, variants all[], int nall, int *processingtype, int *level, char **newtag );
+int get_reftype( const char *q, long refnum, char *progname, variants *all, int nall, char *tag, int *is_default, int chattiness );
+int process_findoldtag( const char *oldtag, int reftype, variants all[], int nall );
+int translate_oldtag( const char *oldtag, int reftype, variants all[], int nall, int *processingtype, int *level, char **newtag );
 
 #endif
diff --git a/bibutils/risin.c b/bibutils/risin.c
--- a/bibutils/risin.c
+++ b/bibutils/risin.c
@@ -1,7 +1,7 @@
 /*
  * risin.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
@@ -30,37 +30,42 @@
 *****************************************************/
 
 static int risin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int risin_processf( fields *risin, char *p, char *filename, long nref, param *pm );
-static int risin_typef( fields *risin, char *filename, int nref, param *p );
+static int risin_processf( fields *risin, const char *p, const char *filename, long nref, param *pm );
+static int risin_typef( fields *risin, const char *filename, int nref, param *p );
 static int risin_convertf( fields *risin, fields *info, int reftype, param *p );
 
-void
-risin_initparams( param *p, const char *progname )
+int
+risin_initparams( param *pm, const char *progname )
 {
-	p->readformat       = BIBL_RISIN;
-	p->charsetin        = BIBL_CHARSET_DEFAULT;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 0;
-	p->xmlin            = 0;
-	p->utf8in           = 0;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->output_raw       = 0;
+	pm->readformat       = BIBL_RISIN;
+	pm->charsetin        = BIBL_CHARSET_DEFAULT;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 0;
+	pm->xmlin            = 0;
+	pm->utf8in           = 0;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->output_raw       = 0;
 
-	p->readf    = risin_readf;
-	p->processf = risin_processf;
-	p->cleanf   = NULL;
-	p->typef    = risin_typef;
-	p->convertf = risin_convertf;
-	p->all      = ris_all;
-	p->nall     = ris_nall;
+	pm->readf    = risin_readf;
+	pm->processf = risin_processf;
+	pm->cleanf   = NULL;
+	pm->typef    = risin_typef;
+	pm->convertf = risin_convertf;
+	pm->all      = ris_all;
+	pm->nall     = ris_nall;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( !pm->progname ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 /*****************************************************
@@ -81,23 +86,24 @@
   www.omicsonline.org mangles the RIS specification and
   puts _three_ spaces before the dash.  Handle this too.
 */
+
 static int
-is_ris_tag( char *buf )
+is_ris_tag( const char *buf )
 {
-	if (! (buf[0]>='A' && buf[0]<='Z') ) return 0;
-	if (! (((buf[1]>='A' && buf[1]<='Z'))||(buf[1]>='0'&&buf[1]<='9')) ) 
-		return 0;
-	if (buf[2]!=' ') return 0;
-	if (buf[3]!=' ') return 0;
+	if ( !isupper( (unsigned char )buf[0] ) ) return 0;
+	if ( !( isupper( (unsigned char )buf[1] ) || isdigit( (unsigned char )buf[1] ) ) ) return 0;
+	if ( buf[2]!=' ' ) return 0;
+	if ( buf[3]!=' ' ) return 0;
 
-	/*...RIS fits specifications with two spaces */
-	if (buf[4]=='-') {
+	/*...RIS tags with two spaces */
+	if ( buf[4]=='-' ) {
 		if ( buf[5]==' ' || buf[5]=='\0' || buf[5]=='\n' || buf[5]=='\r' ) return 1;
 	}
 
-	/* ...extra space in tag? */
-	else if (buf[4]==' ') {
-		if ( buf[5]=='-' && ( buf[6]==' ' || buf[6]=='\0' || buf[6]=='\n' || buf[6]=='\r' ) ) return 1;
+	/* ...RIS tags with three spaces */
+	else if ( buf[4]==' ' ) {
+		if ( buf[5]!='-' ) return 0;
+		if ( buf[6]==' ' || buf[6]=='\0' || buf[6]=='\n' || buf[6]=='\r' ) return 1;
 	}
 
 	return 0;
@@ -142,7 +148,7 @@
 
 		if ( str_is_empty( line ) ) continue;
 
-		p = &( line->data[0] );
+		p = str_cstr( line );
 
 		if ( utf8_is_bom( p ) ) {
 			*fcharset = CHARSET_UNICODE;
@@ -189,8 +195,8 @@
  PUBLIC: int risin_processf()
 *****************************************************/
 
-static char*
-process_untagged_line( str *value, char *p )
+static const char*
+process_untagged_line( str *value, const char *p )
 {
 	while ( *p==' ' || *p=='\t' ) p++;
 	while ( *p && *p!='\r' && *p!='\n' )
@@ -199,8 +205,8 @@
 	return p;
 }
 
-static char*
-process_tagged_line( str *tag, str *value, char *p )
+static const char*
+process_tagged_line( str *tag, str *value, const char *p )
 {
 	int i = 0;
 
@@ -265,7 +271,7 @@
 }
 
 static int
-risin_processf( fields *risin, char *p, char *filename, long nref, param *pm )
+risin_processf( fields *risin, const char *p, const char *filename, long nref, param *pm )
 {
 	int status, tag_added = 0, ret = 1;
 	str tag, value;
@@ -309,7 +315,7 @@
 *****************************************************/
 
 static int
-risin_typef( fields *risin, char *filename, int nref, param *p )
+risin_typef( fields *risin, const char *filename, int nref, param *p )
 {
 	int ntypename, nrefname, is_default;
 	char *refname = "", *typename = "";
@@ -379,7 +385,7 @@
 static int
 risin_date( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
 {
-	char *p = invalue->data;
+	char *p = str_cstr( invalue );
 	int part, status;
 	str date;
 
@@ -531,8 +537,8 @@
 
 	for ( i=0; i<nfields; ++i ) {
 		intag = fields_tag( bibin, i, FIELDS_STRP );
-		if ( !translate_oldtag( intag->data, reftype, p->all, p->nall, &process, &level, &outtag ) ) {
-			risin_report_notag( p, intag->data );
+		if ( !translate_oldtag( str_cstr( intag ), reftype, p->all, p->nall, &process, &level, &outtag ) ) {
+			risin_report_notag( p, str_cstr( intag ) );
 			continue;
 		}
 		invalue = fields_value( bibin, i, FIELDS_STRP );
diff --git a/bibutils/risout.c b/bibutils/risout.c
--- a/bibutils/risout.c
+++ b/bibutils/risout.c
@@ -1,7 +1,7 @@
 /*
  * risout.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
@@ -9,45 +9,62 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
-#include "utf8.h"
-#include "str.h"
-#include "strsearch.h"
+#include "bibformats.h"
 #include "fields.h"
+#include "generic.h"
 #include "name.h"
+#include "str.h"
 #include "title.h"
 #include "url.h"
-#include "bibformats.h"
+#include "utf8.h"
 
-static int  risout_write( fields *info, FILE *fp, param *p, unsigned long refnum );
-static void risout_writeheader( FILE *outptr, param *p );
+/*****************************************************
+ PUBLIC: int risout_initparams()
+*****************************************************/
 
+static int  risout_write( fields *info, FILE *fp, param *p, unsigned long refnum );
+static int  risout_assemble( fields *in, fields *out, param *pm, unsigned long refnum );
 
-void
-risout_initparams( param *p, const char *progname )
+int
+risout_initparams( param *pm, const char *progname )
 {
-	p->writeformat      = BIBL_RISOUT;
-	p->format_opts      = 0;
-	p->charsetout       = BIBL_CHARSET_DEFAULT;
-	p->charsetout_src   = BIBL_SRC_DEFAULT;
-	p->latexout         = 0;
-	p->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
-	p->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
-	p->xmlout           = BIBL_XMLOUT_FALSE;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->singlerefperfile = 0;
+	pm->writeformat      = BIBL_RISOUT;
+	pm->format_opts      = 0;
+	pm->charsetout       = BIBL_CHARSET_DEFAULT;
+	pm->charsetout_src   = BIBL_SRC_DEFAULT;
+	pm->latexout         = 0;
+	pm->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
+	pm->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
+	pm->xmlout           = BIBL_XMLOUT_FALSE;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->singlerefperfile = 0;
 
-	if ( p->charsetout == BIBL_CHARSET_UNICODE ) {
-		p->utf8out = p->utf8bom = 1;
+	if ( pm->charsetout == BIBL_CHARSET_UNICODE ) {
+		pm->utf8out = pm->utf8bom = 1;
 	}
 
-	p->headerf = risout_writeheader;
-	p->footerf = NULL;
-	p->writef  = risout_write;
+	pm->headerf   = generic_writeheader;
+	pm->footerf   = NULL;
+	pm->assemblef = risout_assemble;
+	pm->writef    = risout_write;
+
+	if ( !pm->progname ) {
+		if ( progname==NULL ) pm->progname = NULL;
+		else {
+			pm->progname = strdup( progname );
+			if ( !pm->progname ) return BIBL_ERR_MEMERR;
+		}
+	}
+
+	return BIBL_OK;
 }
 
+/*****************************************************
+ PUBLIC: int risout_assemble()
+*****************************************************/
+
 enum { 
 	TYPE_UNKNOWN = 0,
 	TYPE_STD,                /* standard/generic */
@@ -654,30 +671,14 @@
 	append_easyall( in, "EDITOR:ASIS", "A3", LEVEL_SERIES, out, status );
 }
 
-static void
-output( FILE *fp, fields *out )
-{
-	char *tag, *value;
-	int i;
-
-	for ( i=0; i<out->n; ++i ) {
-		tag   = fields_tag( out, i, FIELDS_CHRP );
-		value = fields_value( out, i, FIELDS_CHRP );
-		fprintf( fp, "%s  - %s\n", tag, value );
-	}
-
-	fprintf( fp, "ER  - \n" );
-	fflush( fp );
-}
-
 static int
-append_data( fields *in, param *p, fields *out )
+risout_assemble( fields *in, fields *out, param *pm, unsigned long refnum )
 {
 	int type, status = BIBL_OK;
 
-	type = get_type( in, p );
+	type = get_type( in, pm );
 
-	append_type      ( type, p, out, &status );
+	append_type      ( type, pm, out, &status );
 	append_allpeople ( in, type, out, &status );
 	append_date      ( in, out, &status );
 	append_alltitles ( in, type, out, &status );
@@ -687,7 +688,7 @@
 	append_easy      ( in, "NUMBER",             "IS", LEVEL_ANY, out, &status );
 	append_easy      ( in, "EDITION",            "ET", LEVEL_ANY, out, &status );
 	append_easy      ( in, "NUMVOLUMES",         "NV", LEVEL_ANY, out, &status );
-	append_easy      ( in, "AUTHORADDRESS",      "AD", LEVEL_ANY, out, &status );
+	append_easy      ( in, "ADDRESS:AUTHOR",     "AD", LEVEL_ANY, out, &status );
 	append_easy      ( in, "PUBLISHER",          "PB", LEVEL_ANY, out, &status );
 	append_easy      ( in, "DEGREEGRANTOR",      "PB", LEVEL_ANY, out, &status );
 	append_easy      ( in, "DEGREEGRANTOR:ASIS", "PB", LEVEL_ANY, out, &status );
@@ -711,24 +712,23 @@
 	return status;
 }
 
+/*****************************************************
+ PUBLIC: int risout_write()
+*****************************************************/
+
 static int
-risout_write( fields *in, FILE *fp, param *p, unsigned long refnum )
+risout_write( fields *out, FILE *fp, param *p, unsigned long refnum )
 {
-	int status;
-	fields out;
-
-	fields_init( &out );
-
-	status = append_data( in, p, &out );
-	if ( status==BIBL_OK ) output( fp, &out );
-
-	fields_free( &out );
+	const char *tag, *value;
+	int i;
 
-	return status;
-}
+	for ( i=0; i<out->n; ++i ) {
+		tag   = fields_tag  ( out, i, FIELDS_CHRP );
+		value = fields_value( out, i, FIELDS_CHRP );
+		fprintf( fp, "%s  - %s\n", tag, value );
+	}
 
-static void
-risout_writeheader( FILE *outptr, param *p )
-{
-	if ( p->utf8bom ) utf8_writebom( outptr );
+	fprintf( fp, "ER  - \n" );
+	fflush( fp );
+	return BIBL_OK;
 }
diff --git a/bibutils/ristypes.c b/bibutils/ristypes.c
--- a/bibutils/ristypes.c
+++ b/bibutils/ristypes.c
@@ -1,7 +1,7 @@
 /*
  * ristypes.c
  *
- * Copyright (c) Chris Putnam 2003-2018
+ * Copyright (c) Chris Putnam 2003-2019
  *
  * Source code released under the GPL version 2
  *
@@ -18,7 +18,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author -- Series editors */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_HOST },   /* Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -93,7 +93,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author -- Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
 	{ "C2", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -174,7 +174,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editors */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
 	{ "C2", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -252,7 +252,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
 	{ "C2", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -330,7 +330,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_HOST },   /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -408,7 +408,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_HOST },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -487,7 +487,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
 	{ "C2", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -565,7 +565,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_HOST },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -643,7 +643,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -720,7 +720,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -797,7 +797,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -874,7 +874,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -952,7 +952,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -1030,7 +1030,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -1106,7 +1106,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -1182,7 +1182,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -1259,7 +1259,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -1336,7 +1336,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -1413,7 +1413,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_HOST },   /* Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
@@ -1490,7 +1490,7 @@
 	{ "A3", "EDITOR",       PERSON,  LEVEL_SERIES }, /* 'Tertiary' Author - Series editor */
 	{ "A4", "AUTHOR",       PERSON,  LEVEL_SERIES }, /* 'Subsidiary' Author */
 	{ "AB", "ABSTRACT",     SIMPLE,  LEVEL_MAIN },   /* Abstract */
-	{ "AD", "AUTHORADDRESS",SIMPLE,  LEVEL_MAIN },   /* Author Address */
+	{ "AD", "ADDRESS:AUTHOR",SIMPLE,  LEVEL_MAIN },   /* Author Address */
 	{ "AU", "AUTHOR",       PERSON,  LEVEL_MAIN },   /* Author */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_MAIN },   /* Book Title - Deprecated? */
 	{ "C1", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'Custom' - put in "notes" */
diff --git a/bibutils/serialno.c b/bibutils/serialno.c
--- a/bibutils/serialno.c
+++ b/bibutils/serialno.c
@@ -1,7 +1,7 @@
 /*
  * serialno.c
  *
- * Copyright (c) Chris Putnam 2005-2018
+ * Copyright (c) Chris Putnam 2005-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/serialno.h b/bibutils/serialno.h
--- a/bibutils/serialno.h
+++ b/bibutils/serialno.h
@@ -1,16 +1,16 @@
 /*
  * serialno.h
  *
- * Copyright (c) Chris Putnam 2005-2018
+ * Copyright (c) Chris Putnam 2005-2019
  *
  * Source code released under the GPL version 2
  *
  */
 #ifndef SERIALNO_H
 #define SERIALNO_H
-#include <stdio.h>
+
 #include "fields.h"
 
-extern int addsn( fields *info, char *buf, int level );
+int addsn( fields *info, char *buf, int level );
 
 #endif
diff --git a/bibutils/slist.c b/bibutils/slist.c
--- a/bibutils/slist.c
+++ b/bibutils/slist.c
@@ -1,9 +1,9 @@
 /*
  * slist.c
  *
- * version: 2017-11-14
+ * version: 2019-01-14
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
@@ -49,7 +49,7 @@
 {
 	int status = SLIST_OK;
 	va_list ap;
-	str *s, *t;
+	str *s;
 
 	slist_init( a );
 
@@ -57,11 +57,8 @@
 	do {
 		s = va_arg( ap, str * );
 		if ( s ) {
-			t = slist_add( a, s );
-			if ( !t ) {
-				status = SLIST_ERR_MEMERR;
-				goto out;
-			}
+			status = slist_add( a, s );
+			if ( status!=SLIST_OK ) goto out;
 		}
 	} while ( s );
 out:
@@ -76,7 +73,6 @@
 	int status = SLIST_OK;
 	va_list ap;
 	char *s;
-	str *t;
 
 	slist_init( a );
 
@@ -84,11 +80,8 @@
 	do {
 		s = va_arg( ap, char * );
 		if ( s ) {
-			t = slist_addc( a, s );
-			if ( !t ) {
-				status = SLIST_ERR_MEMERR;
-				goto out;
-			}
+			status = slist_addc( a, s );
+			if ( status!=SLIST_OK ) goto out;
 		}
 	} while ( s );
 out:
@@ -161,6 +154,23 @@
 }
 
 static int
+slist_revcomp( const void *v1, const void *v2 )
+{
+	str *s1 = ( str *) v1;
+	str *s2 = ( str *) v2;
+	int n;
+
+	if ( !s1->len && !s2->len ) return 0;
+	else if ( !s1->len ) return 1;
+	else if ( !s2->len ) return -1;
+
+	n = str_strcmp( s1, s2 );
+	if ( n==0 ) return 0;
+	else if ( n > 0 ) return -1;
+	else return 1;
+}
+
+static int
 slist_comp( const void *v1, const void *v2 )
 {
 	str *s1 = ( str *) v1;
@@ -318,7 +328,7 @@
 	return status;
 }
 
-str *
+int
 slist_addvp( slist *a, int mode, void *vp )
 {
 	str *s = NULL;
@@ -335,7 +345,7 @@
 		else
 			str_strcpy( s, (str*) vp );
 
-		if ( str_memerr( s ) ) return NULL;
+		if ( str_memerr( s ) ) return SLIST_ERR_MEMERR;
 		a->n++;
 		if ( a->sorted && a->n > 1 ) {
 			if ( slist_comp_step( a, a->n-2, a->n-1 ) > 0 )
@@ -344,23 +354,44 @@
 
 	}
 
-	return s;
+	return SLIST_OK;
 }
-str *
+int
 slist_addc( slist *a, const char *s )
 {
 	return slist_addvp( a, SLIST_CHR, (void*)s );
 }
-str *
+int
 slist_add( slist *a, str *s )
 {
 	return slist_addvp( a, SLIST_STR, (void*)s );
 }
 
-str *
+int
+slist_addvp_ret( slist *a, int mode, void *vp, int retok, int reterr )
+{
+	int status = slist_addvp( a, mode, vp );
+	if ( status==SLIST_OK ) return retok;
+	else return reterr;
+}
+int
+slist_addc_ret( slist *a, const char *value, int retok, int reterr )
+{
+	int status = slist_addc( a, value );
+	if ( status==SLIST_OK ) return retok;
+	else return reterr;
+}
+int
+slist_add_ret( slist *a, str *value, int retok, int reterr )
+{
+	int status = slist_add( a, value );
+	if ( status==SLIST_OK ) return retok;
+	else return reterr;
+}
+
+int
 slist_addvp_unique( slist *a, int mode, void *vp )
 {
-	str *s;
 	int n;
 
 	if ( mode==SLIST_CHR )
@@ -369,30 +400,49 @@
 		n = slist_find( a, (str*) vp );
 
 	if ( slist_wasfound( a, n ) )
-		s = &( a->strs[n] );
+		return SLIST_OK;
 	else
-		s = slist_addvp( a, mode, vp );
-
-	return s;
+		return slist_addvp( a, mode, vp );
 }
-str *
+int
 slist_addc_unique( slist *a, const char *s )
 {
 	return slist_addvp_unique( a, SLIST_CHR, (void*)s );
 }
-str *
+int
 slist_add_unique( slist *a, str *s )
 {
 	return slist_addvp_unique( a, SLIST_STR, (void*)s );
 }
 
 int
+slist_addvp_unique_ret( slist *a, int mode, void *vp, int retok, int reterr )
+{
+	int status = slist_addvp_unique( a, mode, vp );
+	if ( status==SLIST_OK ) return retok;
+	else return reterr;
+}
+int
+slist_addc_unique_ret( slist *a, const char *s, int retok, int reterr )
+{
+	int status = slist_addc_unique( a, s );
+	if ( status==SLIST_OK ) return retok;
+	else return reterr;
+}
+int
+slist_add_unique_ret( slist *a, str *s, int retok, int reterr )
+{
+	int status = slist_add_unique( a, s );
+	if ( status==SLIST_OK ) return retok;
+	else return reterr;
+}
+
+int
 slist_addvp_all( slist *a, int mode, ... )
 {
-	int ret = SLIST_OK;
+	int status = SLIST_OK;
 	va_list ap;
 	void *v;
-	str *s;
 
 	va_start( ap, mode );
 
@@ -404,26 +454,23 @@
 			v = va_arg( ap, str * );
 
 		if ( v ) {
-			s = slist_addvp( a, mode, v );
-			if ( s==NULL ) {
-				ret = SLIST_ERR_MEMERR;
-				goto out;
-			}
+			status = slist_addvp( a, mode, v );
+			if ( status!=SLIST_OK ) goto out;
 		}
 
 	} while ( v );
 
 out:
 	va_end( ap );
-	return ret;
+	return status;
 }
 
 int
 slist_add_all( slist *a, ... )
 {
-	int ret = SLIST_OK;
+	int status = SLIST_OK;
 	va_list ap;
-	str *s, *v;
+	str *v;
 
 	va_start( ap, a );
 
@@ -431,26 +478,22 @@
 		v = va_arg( ap, str * );
 
 		if ( v ) {
-			s = slist_addvp( a, SLIST_STR, (void*)v );
-			if ( s==NULL ) {
-				ret = SLIST_ERR_MEMERR;
-				goto out;
-			}
+			status = slist_addvp( a, SLIST_STR, (void*)v );
+			if ( status!=SLIST_OK ) goto out;
 		}
 
 	} while ( v );
 out:
 	va_end( ap );
-	return ret;
+	return status;
 }
 
 int
 slist_addc_all( slist *a, ... )
 {
-	int ret = SLIST_OK;
+	int status = SLIST_OK;
 	const char *v;
 	va_list ap;
-	str *s;
 
 	va_start( ap, a );
 
@@ -459,17 +502,14 @@
 		v = va_arg( ap, const char * );
 
 		if ( v ) {
-			s = slist_addvp( a, SLIST_CHR, (void*)v );
-			if ( s==NULL ) {
-				ret = SLIST_ERR_MEMERR;
-				goto out;
-			}
+			status = slist_addvp( a, SLIST_CHR, (void*)v );
+			if ( status!=SLIST_OK ) goto out;
 		}
 
 	} while ( v );
 out:
 	va_end( ap );
-	return ret;
+	return status;
 }
 
 int
@@ -489,6 +529,13 @@
 			if ( str_memerr( &(a->strs[a->n+i]) ) ) return SLIST_ERR_MEMERR;
 		}
 
+		if ( a->sorted && toadd->sorted == 0 ) a->sorted = 0;
+		if ( a->sorted && a->n > 0 ) {
+			if ( slist_comp_step( a, a->n-1, a->n ) > 0 ) {
+				a->sorted = 0;
+			}
+		}
+
 		a->n += toadd->n;
 
 	}
@@ -499,21 +546,40 @@
 int
 slist_append_unique( slist *a, slist *toadd )
 {
-	str *s;
-	int i;
+	int i, status;
 
 	assert( a );
 	assert( toadd );
 
 	for ( i=0; i<toadd->n; ++i ) {
-		s = slist_add_unique( a, &(toadd->strs[i]) );
-		if ( !s ) return SLIST_ERR_MEMERR;
+		status = slist_add_unique( a, &(toadd->strs[i]) );
+		if ( status!=SLIST_OK ) return status;
 	}
 
 	return SLIST_OK;
 }
 
 int
+slist_append_ret( slist *a, slist *toadd, int retok, int reterr )
+{
+	int status;
+
+	status = slist_append( a, toadd );
+	if ( status==SLIST_OK ) return retok;
+	else return reterr;
+}
+
+int
+slist_append_unique_ret( slist *a, slist *toadd, int retok, int reterr )
+{
+	int status;
+
+	status = slist_append_unique( a, toadd );
+	if ( status==SLIST_OK ) return retok;
+	else return reterr;
+}
+
+int
 slist_remove( slist *a, slist_index n )
 {
 	int i;
@@ -539,6 +605,13 @@
 	a->sorted = 1;
 }
 
+void
+slist_revsort( slist *a )
+{
+	qsort( a->strs, a->n, sizeof( str ), slist_revcomp );
+	a->sorted = 0;
+}
+
 static slist_index
 slist_find_sorted( slist *a, const char *searchstr )
 {
@@ -636,8 +709,8 @@
 int
 slist_fillfp( slist *a, FILE *fp, unsigned char skip_blank_lines )
 {
-	int ret = SLIST_OK;
-	str line, *s;
+	int status, ret = SLIST_OK;
+	str line;
 
 	assert( a );
 	assert( fp );
@@ -647,8 +720,8 @@
 
 	while ( str_fgetline( &line, fp ) ) {
 		if ( skip_blank_lines && line.len==0 ) continue;
-		s = slist_add( a, &line );
-		if ( !s ) {
+		status = slist_add( a, &line );
+		if ( status!=SLIST_OK ) {
 			ret = SLIST_ERR_MEMERR;
 			goto out;
 		}
@@ -704,6 +777,14 @@
 	return SLIST_OK;
 }
 
+int
+slist_copy_ret( slist *to, slist *from, int retok, int reterr )
+{
+	int status = slist_copy( to, from );
+	if ( status==SLIST_OK ) return retok;
+	else return reterr;
+}
+
 slist *
 slist_dup( slist *from )
 {
@@ -788,9 +869,9 @@
 int
 slist_tokenizec( slist *tokens, char *p, const char *delim, int merge_delim )
 {
-	int ret = SLIST_OK;
-	str s, *t;
+	int status, ret = SLIST_OK;
 	char *q;
+	str s;
 
 	assert( tokens );
 
@@ -802,11 +883,11 @@
 		str_segcpy( &s, p, q );
 		if ( str_memerr( &s ) ) { ret = SLIST_ERR_MEMERR; goto out; }
 		if ( s.len ) {
-			t = slist_addvp( tokens, SLIST_STR, (void*) &s );
-			if ( !t ) { ret = SLIST_ERR_MEMERR; goto out; }
+			status = slist_addvp( tokens, SLIST_STR, (void*) &s );
+			if ( status!=SLIST_OK ) { ret = SLIST_ERR_MEMERR; goto out; }
 		} else if ( !merge_delim ) {
-			t = slist_addvp( tokens, SLIST_CHR, (void*) "" );
-			if ( !t ) { ret = SLIST_ERR_MEMERR; goto out; }
+			status = slist_addvp( tokens, SLIST_CHR, (void*) "" );
+			if ( status!=SLIST_OK ) { ret = SLIST_ERR_MEMERR; goto out; }
 		}
 		p = q;
 		if ( *p ) p++;
diff --git a/bibutils/slist.h b/bibutils/slist.h
--- a/bibutils/slist.h
+++ b/bibutils/slist.h
@@ -1,9 +1,9 @@
 /*
  * slist.h
  *
- * version: 2017-11-14
+ * version: 2019-01-14
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
@@ -52,22 +52,33 @@
 
 slist * slist_dup( slist *a );
 int     slist_copy( slist *to, slist *from );
+int     slist_copy_ret( slist *to, slist *from, int retok, int reterr );
 void    slist_swap( slist *a, slist_index n1, slist_index n2 );
 
-str *   slist_addvp( slist *a, int mode, void *vp );
-str *   slist_addc( slist *a, const char *value );
-str *   slist_add( slist *a, str *value );
+int     slist_addvp( slist *a, int mode, void *vp );
+int     slist_addc( slist *a, const char *value );
+int     slist_add( slist *a, str *value );
 
+int     slist_addvp_ret( slist *a, int mode, void *vp, int retok, int reterr );
+int     slist_addc_ret( slist *a, const char *value, int retok, int reterr );
+int     slist_add_ret( slist *a, str *value, int retok, int reterr );
+
 int     slist_addvp_all( slist *a, int mode, ... );
 int     slist_addc_all( slist *a, ... );
 int     slist_add_all( slist *a, ... );
 
-str *   slist_addvp_unique( slist *a, int mode, void *vp );
-str *   slist_addc_unique( slist *a, const char *value );
-str *   slist_add_unique( slist *a, str *value );
+int     slist_addvp_unique( slist *a, int mode, void *vp );
+int     slist_addc_unique( slist *a, const char *value );
+int     slist_add_unique( slist *a, str *value );
 
+int     slist_addvp_unique_ret( slist *a, int mode, void *vp, int retok, int reterr );
+int     slist_addc_unique_ret( slist *a, const char *value, int retok, int reterr );
+int     slist_add_unique_ret( slist *a, str *value, int retok, int reterr );
+
 int     slist_append( slist *a, slist *toadd );
+int     slist_append_ret( slist *a, slist *toadd, int retok, int reterr );
 int     slist_append_unique( slist *a, slist *toadd );
+int     slist_append_unique_ret( slist *a, slist *toadd, int retok, int reterr );
 
 int     slist_remove( slist *a, slist_index n );
 
@@ -78,6 +89,7 @@
 str *   slist_setc( slist *a, slist_index n, const char *s );
 
 void    slist_sort( slist *a );
+void    slist_revsort( slist *a );
 
 int     slist_find( slist *a, str *searchstr );
 int     slist_findc( slist *a, const char *searchstr );
diff --git a/bibutils/str.c b/bibutils/str.c
--- a/bibutils/str.c
+++ b/bibutils/str.c
@@ -1,9 +1,9 @@
 /*
  * str.c
  *
- * Version: 2018-08-03
+ * Version: 2018-09-21
  *
- * Copyright (c) Chris Putnam 1999-2018
+ * Copyright (c) Chris Putnam 1999-2019
  *
  * Source code released under the GPL version 2
  *
@@ -210,7 +210,7 @@
 	unsigned long size = str_initlen;
 	assert( s );
 	if ( minsize > str_initlen ) size = minsize;
-	s->data = (char *) malloc (sizeof( *(s->data) ) * size);
+	s->data = (char *) malloc( sizeof( *(s->data) ) * size );
 	if ( !s->data ) {
 		fprintf(stderr,"Error.  Cannot allocate memory in str_initalloc, requested %lu characters.\n", size );
 		exit( EXIT_FAILURE );
@@ -218,6 +218,7 @@
 	s->data[0]='\0';
 	s->dim=size;
 	s->len=0;
+	str_clear_status( s );
 }
 
 str *
@@ -449,8 +450,8 @@
  *     finalstep = set to non-zero to position return value past the
  *                 terminating character
  */
-char *
-str_cpytodelim( str *s, char *p, const char *delim, unsigned char finalstep )
+const char *
+str_cpytodelim( str *s, const char *p, const char *delim, unsigned char finalstep )
 {
 	assert( s );
 
@@ -463,8 +464,8 @@
  *     finalstep = set to non-zero to position return value past the
  *                 terminating character
  */
-char *
-str_cattodelim( str *s, char *p, const char *delim, unsigned char finalstep )
+const char *
+str_cattodelim( str *s, const char *p, const char *delim, unsigned char finalstep )
 {
 	assert( s );
 
diff --git a/bibutils/str.h b/bibutils/str.h
--- a/bibutils/str.h
+++ b/bibutils/str.h
@@ -1,9 +1,9 @@
 /*
  * str.h
  *
- * Version: 2018-08-03
+ * Version: 2018-09-21
  *
- * Copyright (c) Chris Putnam 1999-2018
+ * Copyright (c) Chris Putnam 1999-2019
  *
  * Source code released under the GPL version 2
  *
@@ -67,8 +67,8 @@
 void str_reverse     ( str *s );
 const char *str_addutf8    ( str *s, const char *p );
 void str_segcat      ( str *s, char *startat, char *endat );
-char *str_cpytodelim  ( str *s, char *p, const char *delim, unsigned char finalstep );
-char *str_cattodelim  ( str *s, char *p, const char *delim, unsigned char finalstep );
+const char *str_cpytodelim  ( str *s, const char *p, const char *delim, unsigned char finalstep );
+const char *str_cattodelim  ( str *s, const char *p, const char *delim, unsigned char finalstep );
 void str_prepend     ( str *s, const char *addstr );
 void str_segcpy      ( str *s, char *startat, char *endat );
 void str_segdel      ( str *s, char *startat, char *endat );
diff --git a/bibutils/str_conv.c b/bibutils/str_conv.c
--- a/bibutils/str_conv.c
+++ b/bibutils/str_conv.c
@@ -1,7 +1,7 @@
 /*
  * str_conv.c
  *
- * Copyright (c) Chris Putnam 1999-2018
+ * Copyright (c) Chris Putnam 1999-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/str_conv.h b/bibutils/str_conv.h
--- a/bibutils/str_conv.h
+++ b/bibutils/str_conv.h
@@ -1,7 +1,7 @@
 /*
  * str_conv.h
  *
- * Copyright (c) Chris Putnam 1999-2018
+ * Copyright (c) Chris Putnam 1999-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/strsearch.c b/bibutils/strsearch.c
--- a/bibutils/strsearch.c
+++ b/bibutils/strsearch.c
@@ -1,6 +1,6 @@
 /* strsearch.c
  *
- * Copyright (c) Chris Putnam 1995-2018
+ * Copyright (c) Chris Putnam 1995-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/strsearch.h b/bibutils/strsearch.h
--- a/bibutils/strsearch.h
+++ b/bibutils/strsearch.h
@@ -1,7 +1,7 @@
 /*
  * strsearch.h
  *
- * Copyright (c) Chris Putnam 1995-2018
+ * Copyright (c) Chris Putnam 1995-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/title.c b/bibutils/title.c
--- a/bibutils/title.c
+++ b/bibutils/title.c
@@ -3,7 +3,7 @@
  *
  * process titles into title/subtitle pairs for MODS
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
@@ -17,11 +17,10 @@
 #include "is_ws.h"
 
 int
-title_process( fields *info, char *tag, char *data, int level, 
-	unsigned char nosplittitle )
+title_process( fields *info, const char *tag, const char *value, int level, unsigned char nosplittitle )
 {
 	str title, subtitle;
-	char *p, *q;
+	const char *p, *q;
 	int status;
 
 	str_init( &title );
@@ -29,13 +28,13 @@
 
 	if ( nosplittitle ) q = NULL;
 	else {
-		q = strstr( data, ": " );
-		if ( !q ) q = strstr( data, "? " );
+		q = strstr( value, ": " );
+		if ( !q ) q = strstr( value, "? " );
 	}
 
-	if ( !q ) str_strcpyc( &title, data );
+	if ( !q ) str_strcpyc( &title, value );
 	else {
-		p = data;
+		p = value;
 		while ( p!=q ) str_addchar( &title, *p++ );
 		if ( *q=='?' ) str_addchar( &title, '?' );
 		q++;
diff --git a/bibutils/title.h b/bibutils/title.h
--- a/bibutils/title.h
+++ b/bibutils/title.h
@@ -3,7 +3,7 @@
  *
  * process titles into title/subtitle pairs for MODS
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL verison 2
  *
@@ -14,7 +14,7 @@
 #include "str.h"
 #include "fields.h"
 
-int  title_process( fields *info, char *tag, char *data, int level, unsigned char nosplittitle );
+int  title_process( fields *info, const char *tag, const char *value, int level, unsigned char nosplittitle );
 void title_combine( str *fullttl, str *mainttl, str *subttl );
 
 #endif
diff --git a/bibutils/type.c b/bibutils/type.c
new file mode 100644
--- /dev/null
+++ b/bibutils/type.c
@@ -0,0 +1,86 @@
+/*
+ * type.c
+ *
+ * Copyright (c) Chris Putnam 2003-2019
+ *
+ * Source code released under the GPL version 2
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "type.h"
+
+static int
+is_genre_element( fields *in, int n )
+{
+	char *tag;
+
+	tag = fields_tag( in, n, FIELDS_CHRP );
+
+	if ( !strcasecmp( tag, "GENRE:MARC"     ) ) return 1;
+	if ( !strcasecmp( tag, "GENRE:BIBUTILS" ) ) return 1;
+	if ( !strcasecmp( tag, "GENRE:UNKNOWN"  ) ) return 1;
+
+	return 0;
+}
+
+static int
+is_resource_element( fields *in, int n )
+{
+	if ( !strcasecmp( fields_tag( in, n, FIELDS_CHRP ), "RESOURCE" ) ) return 1;
+	return 0;
+}
+
+static int
+is_issuance_element( fields *in, int n )
+{
+	if ( !strcasecmp( fields_tag( in, n, FIELDS_CHRP ), "ISSUANCE" ) ) return 1;
+	else return 0;
+}
+
+static int
+match_hints( const char *value, int level, const char *match_name, int match_level )
+{
+	if ( strcasecmp( value, match_name ) ) return 0;
+	if ( match_level!=LEVEL_ANY && level!=match_level ) return 0;
+	return 1;
+}
+
+/* type_from_mods_hints()
+ *
+ * We return the first match from the match list that works...this makes us
+ * independent of how the genre hints are internally stored in fields *in.
+ *
+ * Thus we can distinguish between 'book' and 'book chapter' in which book is
+ * at different MODS levels by match_type arrays of:
+ *
+ * ...
+ * { "book",    TYPE_BOOK,          LEVEL_MAIN },
+ * { "book",    TYPE_BOOKCHAPTER,   LEVEL_ANY  },
+ * ...
+ *
+ * e.g. "book" at LEVEL_ANY matches any values of "book" not caught by the "book" LEVEL_MAIN line
+ *
+ */
+int
+type_from_mods_hints( fields *in, int mode, match_type matches[], int nmatches, int type_unknown )
+{
+	int i, j, level, type = type_unknown;
+	char *value;
+
+	for ( i=0; i<nmatches; ++i ) {
+
+		for ( j=0; j<in->n; ++j ) {
+			if ( mode==TYPE_FROM_GENRE    && !is_genre_element( in, j ) )    continue;
+			if ( mode==TYPE_FROM_RESOURCE && !is_resource_element( in, j ) ) continue;
+			if ( mode==TYPE_FROM_ISSUANCE && !is_issuance_element( in, j ) ) continue;
+			value = fields_value( in, j, FIELDS_CHRP );
+			level = fields_level( in, j );
+			if ( match_hints( value, level, matches[i].name, matches[i].level ) ) {
+				if ( type==type_unknown ) type = matches[i].type;
+			}
+		}
+	}
+
+	return type;
+}
diff --git a/bibutils/type.h b/bibutils/type.h
new file mode 100644
--- /dev/null
+++ b/bibutils/type.h
@@ -0,0 +1,27 @@
+/*
+ * type.h
+ *
+ * Copyright (c) Chris Putnam 2019
+ *
+ * Source code released under the GPL version 2
+ */
+#ifndef TYPE_H
+#define TYPE_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "fields.h"
+
+#define TYPE_FROM_GENRE    (0)
+#define TYPE_FROM_RESOURCE (1)
+#define TYPE_FROM_ISSUANCE (2)
+
+typedef struct match_type {
+        char *name;
+        int type;
+        int level;
+} match_type;
+
+int type_from_mods_hints( fields *in, int mode, match_type matches[], int nmatches, int type_unknown );
+
+#endif
diff --git a/bibutils/unicode.h b/bibutils/unicode.h
--- a/bibutils/unicode.h
+++ b/bibutils/unicode.h
@@ -1,7 +1,7 @@
 /*
  * unicode.h
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Program and source code released under the GPL version 2
  */
diff --git a/bibutils/url.c b/bibutils/url.c
--- a/bibutils/url.c
+++ b/bibutils/url.c
@@ -9,7 +9,7 @@
  * is_doi()
  * Check for DOI buried in another field.
  *
- * Copyright (c) Chris Putnam 2008-2018
+ * Copyright (c) Chris Putnam 2008-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/url.h b/bibutils/url.h
--- a/bibutils/url.h
+++ b/bibutils/url.h
@@ -1,7 +1,7 @@
 /*
  * url.h
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/utf8.c b/bibutils/utf8.c
--- a/bibutils/utf8.c
+++ b/bibutils/utf8.c
@@ -1,7 +1,7 @@
 /*
  * utf8.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
@@ -93,7 +93,7 @@
 }
 
 unsigned int
-utf8_decode( char *s, unsigned int *pi )
+utf8_decode( const char *s, unsigned int *pi )
 {
 	unsigned int c;
 	int i = *pi;
@@ -150,9 +150,9 @@
 }
 
 int
-utf8_is_bom( char *p )
+utf8_is_bom( const char *p )
 {
-	unsigned char *up = ( unsigned char * ) p;
+	const unsigned char *up = ( unsigned char * ) p;
 
 	/* ...if null-terminated string is too short, we're ok */
 	if ( up[0]!=0xEF ) return 0;
@@ -167,7 +167,7 @@
  *emdash = 0xE2 (-30) 0x80 (-128) 0x94 (-108)
  */
 int
-utf8_is_emdash( char *p )
+utf8_is_emdash( const char *p )
 {
 	const char emdash[3] = { -30, -128, -108 };
 	if ( strncmp( p, emdash, 3 ) ) return 0;
@@ -179,7 +179,7 @@
  * endash = 0xE2 (-30) 0x80 (-128) 0x93 (-109)
  */
 int
-utf8_is_endash( char *p )
+utf8_is_endash( const char *p )
 {
 	const char endash[3] = { -30, -128, -109 };
 	if ( strncmp( p, endash, 3 ) ) return 0;
diff --git a/bibutils/utf8.h b/bibutils/utf8.h
--- a/bibutils/utf8.h
+++ b/bibutils/utf8.h
@@ -1,7 +1,7 @@
 /*
  * utf8.h
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
@@ -13,10 +13,10 @@
 
 int          utf8_encode( unsigned int value, unsigned char out[6] );
 void         utf8_encode_str( unsigned int value, char outstr[7] );
-unsigned int utf8_decode( char *s, unsigned int *pi );
+unsigned int utf8_decode( const char *s, unsigned int *pi );
 void         utf8_writebom( FILE *outptr );
-int          utf8_is_bom( char *p );
-int          utf8_is_emdash( char *p );
-int          utf8_is_endash( char *p );
+int          utf8_is_bom( const char *p );
+int          utf8_is_emdash( const char *p );
+int          utf8_is_endash( const char *p );
 
 #endif
diff --git a/bibutils/vplist.c b/bibutils/vplist.c
--- a/bibutils/vplist.c
+++ b/bibutils/vplist.c
@@ -3,7 +3,7 @@
  *
  * Version: 1/9/2017
  *
- * Copyright (c) Chris Putnam 2011-2018
+ * Copyright (c) Chris Putnam 2011-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/vplist.h b/bibutils/vplist.h
--- a/bibutils/vplist.h
+++ b/bibutils/vplist.h
@@ -5,7 +5,7 @@
  *
  * Version: 1/9/2017
  *
- * Copyright (c) Chris Putnam 2011-2018
+ * Copyright (c) Chris Putnam 2011-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/wordin.c b/bibutils/wordin.c
--- a/bibutils/wordin.c
+++ b/bibutils/wordin.c
@@ -1,7 +1,7 @@
 /*
  * wordin.c
  *
- * Copyright (c) Chris Putnam 2010-2018
+ * Copyright (c) Chris Putnam 2010-2019
  *
  * Source code released under the GPL version 2
  *
@@ -17,41 +17,46 @@
 #include "bibformats.h"
 
 static int wordin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset );
-static int wordin_processf( fields *wordin, char *data, char *filename, long nref, param *p );
+static int wordin_processf( fields *wordin, const char *data, const char *filename, long nref, param *p );
 
 
 /*****************************************************
  PUBLIC: void wordin_initparams()
 *****************************************************/
 
-void
-wordin_initparams( param *p, const char *progname )
+int
+wordin_initparams( param *pm, const char *progname )
 {
-	p->readformat       = BIBL_WORDIN;
-	p->charsetin        = BIBL_CHARSET_DEFAULT;
-	p->charsetin_src    = BIBL_SRC_DEFAULT;
-	p->latexin          = 0;
-	p->xmlin            = 1;
-	p->utf8in           = 1;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->output_raw       = BIBL_RAW_WITHMAKEREFID |
+	pm->readformat       = BIBL_WORDIN;
+	pm->charsetin        = BIBL_CHARSET_DEFAULT;
+	pm->charsetin_src    = BIBL_SRC_DEFAULT;
+	pm->latexin          = 0;
+	pm->xmlin            = 1;
+	pm->utf8in           = 1;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->output_raw       = BIBL_RAW_WITHMAKEREFID |
 	                      BIBL_RAW_WITHCHARCONVERT;
 
-	p->readf    = wordin_readf;
-	p->processf = wordin_processf;
-	p->cleanf   = NULL;
-	p->typef    = NULL;
-	p->convertf = NULL;
-	p->all      = NULL;
-	p->nall     = 0;
+	pm->readf    = wordin_readf;
+	pm->processf = wordin_processf;
+	pm->cleanf   = NULL;
+	pm->typef    = NULL;
+	pm->convertf = NULL;
+	pm->all      = NULL;
+	pm->nall     = 0;
 
-	slist_init( &(p->asis) );
-	slist_init( &(p->corps) );
+	slist_init( &(pm->asis) );
+	slist_init( &(pm->corps) );
 
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
+	if ( !progname ) pm->progname = NULL;
+	else {
+		pm->progname = strdup( progname );
+		if ( !pm->progname ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
 }
 
 /*****************************************************
@@ -302,7 +307,7 @@
 }
 
 static int
-wordin_processf( fields *wordin, char *data, char *filename, long nref, param *p )
+wordin_processf( fields *wordin, const char *data, const char *filename, long nref, param *p )
 {
 	int status, ret = 1;
 	xml top;
diff --git a/bibutils/wordout.c b/bibutils/wordout.c
--- a/bibutils/wordout.c
+++ b/bibutils/wordout.c
@@ -3,7 +3,7 @@
  * 
  * (Word 2007 format)
  *
- * Copyright (c) Chris Putnam 2007-2018
+ * Copyright (c) Chris Putnam 2007-2019
  *
  * Source code released under the GPL version 2
  *
@@ -16,34 +16,53 @@
 #include "utf8.h"
 #include "bibformats.h"
 
+/*****************************************************
+ PUBLIC: int wordout_initparams()
+*****************************************************/
+
 static void wordout_writeheader( FILE *outptr, param *p );
 static void wordout_writefooter( FILE *outptr );
 static int  wordout_write( fields *info, FILE *outptr, param *p, unsigned long numrefs );
 
-void
-wordout_initparams( param *p, const char *progname )
+int
+wordout_initparams( param *pm, const char *progname )
 {
-	p->writeformat      = BIBL_WORD2007OUT;
-	p->format_opts      = 0;
-	p->charsetout       = BIBL_CHARSET_UNICODE;
-	p->charsetout_src   = BIBL_SRC_DEFAULT;
-	p->latexout         = 0;
-	p->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
-	p->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
-	if ( !p->utf8out )
-		p->xmlout   = BIBL_XMLOUT_ENTITIES;
+	pm->writeformat      = BIBL_WORD2007OUT;
+	pm->format_opts      = 0;
+	pm->charsetout       = BIBL_CHARSET_UNICODE;
+	pm->charsetout_src   = BIBL_SRC_DEFAULT;
+	pm->latexout         = 0;
+	pm->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
+	pm->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
+	if ( !pm->utf8out )
+		pm->xmlout   = BIBL_XMLOUT_ENTITIES;
 	else
-		p->xmlout   = BIBL_XMLOUT_TRUE;
-	p->nosplittitle     = 0;
-	p->verbose          = 0;
-	p->addcount         = 0;
-	p->singlerefperfile = 0;
+		pm->xmlout   = BIBL_XMLOUT_TRUE;
+	pm->nosplittitle     = 0;
+	pm->verbose          = 0;
+	pm->addcount         = 0;
+	pm->singlerefperfile = 0;
 
-	p->headerf = wordout_writeheader;
-	p->footerf = wordout_writefooter;
-	p->writef  = wordout_write;
+	pm->headerf   = wordout_writeheader;
+	pm->footerf   = wordout_writefooter;
+	pm->assemblef = NULL;
+	pm->writef    = wordout_write;
+
+	if ( !pm->progname ) {
+		if ( !progname ) pm->progname = NULL;
+		else {
+			pm->progname = strdup( progname );
+			if ( !pm->progname ) return BIBL_ERR_MEMERR;
+		}
+	}
+
+	return BIBL_OK;
 }
 
+/*****************************************************
+ PUBLIC: int wordout_write()
+*****************************************************/
+
 typedef struct convert {
 	char *oldtag;
 	char *newtag;
@@ -647,6 +666,10 @@
 	return BIBL_OK;
 }
 
+/*****************************************************
+ PUBLIC: void wordout_writeheader()
+*****************************************************/
+
 static void
 wordout_writeheader( FILE *outptr, param *p )
 {
@@ -656,6 +679,10 @@
 		"xmlns:b=\"http://schemas.openxmlformats.org/officeDocument/2006/bibliography\" "
 		" xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/bibliography\" >\n");
 }
+
+/*****************************************************
+ PUBLIC: void wordout_writefooter()
+*****************************************************/
 
 static void
 wordout_writefooter( FILE *outptr )
diff --git a/bibutils/xml.c b/bibutils/xml.c
--- a/bibutils/xml.c
+++ b/bibutils/xml.c
@@ -1,7 +1,7 @@
 /*
  * xml.c
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
@@ -61,7 +61,7 @@
 };
 
 static int
-xml_is_terminator( char *p, int *type )
+xml_is_terminator( const char *p, int *type )
 {
 	if ( *p=='>' ) {
 		return 1;
@@ -82,27 +82,27 @@
 static int
 xml_add_attribute( xml *node, char *attribute, char *attribute_value  )
 {
-	str *s;
+	int status;
 
 	if ( attribute )
-		s = slist_addc( &(node->attributes), attribute );
+		status = slist_addc( &(node->attributes), attribute );
 	else
-		s = slist_addc( &(node->attributes), "" );
-	if ( s==NULL ) return 0;
+		status = slist_addc( &(node->attributes), "" );
+	if ( status!=SLIST_OK ) return 0;
 
 	if ( attribute_value )
-		s = slist_addc( &(node->attribute_values), attribute_value );
+		status = slist_addc( &(node->attribute_values), attribute_value );
 	else
-		s = slist_addc( &(node->attribute_values), "" );
-	if ( s==NULL ) {
+		status = slist_addc( &(node->attribute_values), "" );
+	if ( status!=SLIST_OK ) {
 		(void) slist_remove( &(node->attributes), node->attributes.n-1 );
 		return 0;
 	}
 	return 1;
 }
 
-static char *
-xml_processattrib( char *p, xml *node, int *type )
+static const char *
+xml_processattrib( const char *p, xml *node, int *type )
 {
 	char quote_character = '\"';
 	int inquotes = 0;
@@ -161,8 +161,8 @@
  * 	XML_CLOSE        </A>
  * 	XML_OPENCLOSE    <A/>
  */
-static char *
-xml_processtag( char *p, xml *node, int *type )
+static const char *
+xml_processtag( const char *p, xml *node, int *type )
 {
 	str tag;
 
@@ -215,8 +215,8 @@
 	}
 }
 
-char *
-xml_parse( char *p, xml *onode )
+const char *
+xml_parse( const char *p, xml *onode )
 {
 	int type, is_style = 0;
 	xml *nnode;
diff --git a/bibutils/xml.h b/bibutils/xml.h
--- a/bibutils/xml.h
+++ b/bibutils/xml.h
@@ -1,7 +1,7 @@
 /*
  * xml.h
  *
- * Copyright (c) Chris Putnam 2004-2018
+ * Copyright (c) Chris Putnam 2004-2019
  *
  * Source code released under the GPL version 2
  *
@@ -35,7 +35,7 @@
 char * xml_find_end             ( char *buffer, char *tag );
 int    xml_tag_has_attribute    ( xml *node, const char *tag, const char *attribute, const char *attribute_value );
 int    xml_has_attribute        ( xml *node, const char *attribute, const char *attribute_value );
-char * xml_parse                ( char *p, xml *onode );
+const char * xml_parse                ( const char *p, xml *onode );
 
 extern char * xml_pns; /* global Namespace */
 
diff --git a/bibutils/xml_encoding.c b/bibutils/xml_encoding.c
--- a/bibutils/xml_encoding.c
+++ b/bibutils/xml_encoding.c
@@ -1,7 +1,7 @@
 /*
  * xml_getencoding.c
  *
- * Copyright (c) Chris Putnam 2007-2018
+ * Copyright (c) Chris Putnam 2007-2019
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/xml_encoding.h b/bibutils/xml_encoding.h
--- a/bibutils/xml_encoding.h
+++ b/bibutils/xml_encoding.h
@@ -1,7 +1,7 @@
 /*
  * xml_getencoding.h
  *
- * Copyright (c) Chris Putnam 2007-2018
+ * Copyright (c) Chris Putnam 2007-2019
  *
  * Source code released under the GPL version 2
  *
@@ -9,6 +9,6 @@
 #ifndef XML_GETENCODING_H
 #define XML_GETENCODING_H
 
-extern int xml_getencoding( str *s );
+int xml_getencoding( str *s );
 
 #endif
diff --git a/hs-bibutils.cabal b/hs-bibutils.cabal
--- a/hs-bibutils.cabal
+++ b/hs-bibutils.cabal
@@ -1,5 +1,5 @@
 name:               hs-bibutils
-version:            6.7.0.0
+version:            6.8.0.0
 homepage:           https://github.com/wilx/hs-bibutils
 
 synopsis:           Haskell bindings to bibutils, the bibliography
@@ -53,11 +53,11 @@
         bibutils/serialno.h bibutils/slist.c bibutils/slist.h bibutils/str.c
         bibutils/str_conv.c bibutils/str_conv.h bibutils/str.h
         bibutils/strsearch.c bibutils/strsearch.h bibutils/title.c
-        bibutils/title.h bibutils/unicode.c bibutils/unicode.h bibutils/url.c
-        bibutils/url.h bibutils/utf8.c bibutils/utf8.h bibutils/vplist.c
-        bibutils/vplist.h bibutils/wordin.c bibutils/wordout.c bibutils/xml.c
-        bibutils/xml_encoding.c bibutils/xml_encoding.h bibutils/xml.h
-        README.md ChangeLog.md
+        bibutils/title.h bibutils/type.c bibutils/type.h bibutils/unicode.c
+        bibutils/unicode.h bibutils/url.c bibutils/url.h bibutils/utf8.c
+        bibutils/utf8.h bibutils/vplist.c bibutils/vplist.h bibutils/wordin.c
+        bibutils/wordout.c bibutils/xml.c bibutils/xml_encoding.c
+        bibutils/xml_encoding.h bibutils/xml.h README.md ChangeLog.md
 
 library
     default-language: Haskell2010
@@ -87,9 +87,10 @@
         bibutils/pages.c bibutils/reftypes.c bibutils/risin.c
         bibutils/risout.c bibutils/ristypes.c bibutils/serialno.c
         bibutils/slist.c bibutils/str.c bibutils/str_conv.c
-        bibutils/strsearch.c bibutils/title.c bibutils/unicode.c
-        bibutils/url.c bibutils/utf8.c bibutils/vplist.c bibutils/wordin.c
-        bibutils/wordout.c bibutils/xml.c bibutils/xml_encoding.c
+        bibutils/strsearch.c bibutils/title.c bibutils/type.c
+        bibutils/unicode.c bibutils/url.c bibutils/utf8.c bibutils/vplist.c
+        bibutils/wordin.c bibutils/wordout.c bibutils/xml.c
+        bibutils/xml_encoding.c
 
     if impl(ghc >= 6.10)
        build-depends: base >= 4, syb
