diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,4 @@
+# 6.2
+
+  - This release has removed `wordout_dropkey` because the corresponding
+    constant in Bibutils has also been removed.
diff --git a/README b/README
deleted file mode 100644
--- a/README
+++ /dev/null
@@ -1,125 +0,0 @@
-% Haskell Bindings to Bibutils, the Bibliography Conversion Utilities
-% Andrea Rossato
-
-About
------
-
-[hs-bibutils] is library with Haskell bindings to Chris Putnam's
-[bibutils], a library that interconverts between various bibliography
-formats using a common MODS-format XML intermediate.
-
-[hs-bibutils] can be used with [citeproc-hs] and [pandoc] to read and
-use various bibliographic database formats for processing and
-automatically formatting citations and references according to a [CSL]
-style.
-
-[citeproc-hs] adds to [pandoc], the famous [Haskell] text processing
-tool, a Bibtex like citation and bibliographic formatting and
-generation facility.
-
-[CSL] is an XML language for specifying citation and bibliographic
-formatting, similar in principle to BibTeX `.bst` files or the binary
-style files in commercial products like Endnote or Reference Manager.
-
-Download
---------
-
-[hs-bibutils] can be downloaded from [Hackage]:
-
-<http://hackage.haskell.org/cgi-bin/hackage-scripts/package/hs-bibutils>
-
-To get the darcs source run:
-
-        darcs get http://code.haskell.org/hs-bibutils/
-
-Installation
-------------
-
-[hs-bibutils] does not require [bibutils] to be installed, since it
-ships with its one version of the library. To build and install
-[hs-bibutils] unpack the tar archive, in the source directory run:
-
-        runhaskell Setup.lhs configure
-        runhaskell Setup.lhs build
-        runhaskell Setup.lhs install
-
-This last step requires `root` privileges.
-
-If you don't have `root` privileges you can install [hs-bibutils]
-locally with these commands:
-
-        runhaskell Setup.lhs configure --user --prefix=$HOME
-        runhaskell Setup.lhs build
-        runhaskell Setup.lhs install --user
-
-[hs-bibutils] should be compatible with cabal-install.
-
-Documentation
--------------
-
-The [Haddock] generated documentation is available here:
-
-<http://code.haskell.org/hs-bibutils/docs/>
-
-The original API documentation is available here:
-
-<http://www.scripps.edu/~cdputnam/software/bibutils/library_specs.html>
-
-Bug Reports
------------
-
-To submit bug reports you can use the [citeproc-hs] bug tracking
-system available at the following address:
-
-<http://code.google.com/p/citeproc-hs/issues>
-
-Credits
--------
-
-Thanks to [Chris Putnam] for his help.
-
-Author
-------
-
-Andrea Rossato
-
-`andrea.rossato at unitn.it`
-
-Links to Related Projects
-----------------
-
-Bibutils
-:    <http://www.scripps.edu/~cdputnam/software/bibutils/>
-
-Pandoc
-:    <http://johnmacfarlane.net/pandoc/>
-
-Citeproc-hs:
-:    <http://code.haskell.org/citeproc-hs/>
-
-CSL
-:    <http://xbiblio.sourceforge.net/csl/>
-
-Legal
------
-
-This software is released under the GNU GPL. See LICENSE for more
-details.
-
-This is an early, "alpha" release. It carries no warranties of any kind.
-
-Copyright &copy; 2008 - 2010 Andrea Rossato
-
-[hs-bibutils]: http://code.haskell.org/hs-bibutils
-[bibutils]: http://www.scripps.edu/~cdputnam/software/bibutils/
-[Hackage]: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/hs-bibutils
-[citeproc-hs]: http://code.haskell.org/citeproc-hs
-[CSL]: http://citationstyles.org/
-[pandoc]: http://johnmacfarlane.net/pandoc/
-[Zotero]: http://www.zotero.org
-[MODS]: http://www.loc.gov/mods/
-[Bruce D'Arcus]: http://community.muohio.edu/blogs/darcusb/
-[John MacFarlane]: http://johnmacfarlane.net/
-[Chris Putnam]: http://www.scripps.edu/~cdputnam/
-[Haskell]:  http://www.haskell.org/
-[Haddock]:  http://www.haskell.org/haddock/
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,95 @@
+About
+-----
+
+[hs-bibutils] is library with Haskell bindings to Chris Putnam's
+[bibutils], a library that interconverts between various bibliography
+formats using a common MODS-format XML intermediate.
+
+[hs-bibutils] can be used with [pandoc-citeproc] to read and use various
+bibliographic database formats for processing and automatically formatting
+citations and references according to a [CSL] style.
+
+[pandoc-citeproc] adds to [pandoc], the famous [Haskell] text processing
+tool, a Bibtex like citation and bibliographic formatting and generation
+facility.
+
+[CSL] is an XML language for specifying citation and bibliographic
+formatting, similar in principle to BibTeX `.bst` files or the binary
+style files in commercial products like Endnote or Reference Manager.
+
+Documentation
+-------------
+
+The [Haddock] generated documentation is available here:
+
+<https://hackage.haskell.org/package/hs-bibutils/docs/Text-Bibutils.html>
+
+Bug Reports
+-----------
+
+To submit bug reports you can use the [hs-bibutils] bug tracking
+system available at the following address:
+
+<https://github.com/wilx/hs-bibutils/issues>
+
+Credits
+-------
+
+Thanks to [Chris Putnam] for his help.
+
+Author
+------
+
+Andrea Rossato
+
+`andrea.rossato at unitn dot it`
+
+Maintainer
+----------
+
+Václav Haisman
+
+`vhaisman at gmail dot com`
+
+Links to Related Projects
+----------------
+
+Bibutils
+:    <https://sourceforge.net/p/bibutils/home/Bibutils/>
+
+Pandoc
+:    <https://pandoc.org/>
+
+Pandoc-Citeproc
+:    <https://github.com/jgm/pandoc-citeproc>
+
+Citeproc-hs:
+:    <http://istitutocolli.org/repos/citeproc-hs/>
+
+CSL
+:    <http://xbiblio.sourceforge.net/csl/>
+
+Legal
+-----
+
+This software is released under the GNU GPL. See LICENSE for more
+details.
+
+This is an early, "alpha" release. It carries no warranties of any kind.
+
+Copyright © 2008 – 2010 Andrea Rossato
+
+[hs-bibutils]: https://github.com/wilx/hs-bibutils
+[bibutils]: https://sourceforge.net/p/bibutils/home/Bibutils/
+[Hackage]: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/hs-bibutils
+[citeproc-hs]: http://istitutocolli.org/repos/citeproc-hs/
+[CSL]: http://citationstyles.org/
+[pandoc]: http://pandoc.org/
+[pandoc-citeproc]: https://github.com/jgm/pandoc-citeproc
+[Zotero]: http://www.zotero.org
+[MODS]: http://www.loc.gov/mods/
+[Bruce D'Arcus]: http://community.muohio.edu/blogs/darcusb/
+[John MacFarlane]: http://johnmacfarlane.net/
+[Chris Putnam]: http://www.scripps.edu/~cdputnam/
+[Haskell]:  http://www.haskell.org/
+[Haddock]:  http://www.haskell.org/haddock/
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-2014
- * Copyright (c) Chris Putnam 2007-2014
+ * Copyright (c) Richard Mathar 2007-2017
+ * Copyright (c) Chris Putnam 2007-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -13,12 +13,17 @@
 #include <string.h>
 #include <ctype.h>
 #include "utf8.h"
-#include "newstr.h"
+#include "str.h"
 #include "strsearch.h"
 #include "fields.h"
 #include "name.h"
-#include "adsout.h"
+#include "title.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 );
+
 void
 adsout_initparams( param *p, const char *progname )
 {
@@ -76,7 +81,7 @@
 } match_type;
 
 static int
-get_type( fields *info )
+get_type( fields *in )
 {
 	match_type match_genres[] = {
 		{ "academic journal",          TYPE_ARTICLE },
@@ -101,15 +106,15 @@
 	char *tag, *data;
 	int i, j, type = TYPE_UNKNOWN;
 
-	for ( i=0; i<info->n; ++i ) {
-		tag = info->tag[i].data;
+	for ( i=0; i<in->n; ++i ) {
+		tag = in->tag[i].data;
 		if ( strcasecmp( tag, "GENRE" )!=0 &&
 		     strcasecmp( tag, "NGENRE" )!=0 ) continue;
-		data = info->data[i].data;
+		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( info, i );
+				fields_setused( in, i );
 			}
 		}
 		if ( type==TYPE_UNKNOWN ) {
@@ -118,26 +123,26 @@
 			else if ( !strcasecmp( data, "thesis" ) )
 				type = TYPE_THESIS;
 			else if ( !strcasecmp( data, "book" ) ) {
-				if ( info->level[i]==0 ) type = TYPE_BOOK;
+				if ( in->level[i]==0 ) type = TYPE_BOOK;
 				else type = TYPE_INBOOK;
 			}
 			else if ( !strcasecmp( data, "collection" ) ) {
-				if ( info->level[i]==0 ) type = TYPE_BOOK;
+				if ( in->level[i]==0 ) type = TYPE_BOOK;
 				else type = TYPE_INBOOK;
 			}
-			if ( type!=TYPE_UNKNOWN ) fields_setused( info, i );
+			if ( type!=TYPE_UNKNOWN ) fields_setused( in, i );
 		}
 	}
 	if ( type==TYPE_UNKNOWN ) {
-		for ( i=0; i<info->n; ++i ) {
-			if ( strcasecmp( info->tag[i].data, "RESOURCE" ) )
+		for ( i=0; i<in->n; ++i ) {
+			if ( strcasecmp( in->tag[i].data, "RESOURCE" ) )
 				continue;
-			data = info->data[i].data;
+			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( info, i );
+			if ( type!=TYPE_UNKNOWN ) fields_setused( in, i );
 		}
 	}
 
@@ -147,81 +152,144 @@
 	return type;
 }
 
-static void
-output_title( FILE *fp, fields *f, char *full, char *sub, char *adstag, int level )
+static int
+append_title( fields *in, char *ttl, char *sub, char *adstag, int level, fields *out, int *status )
 {
-	newstr *fulltitle, *subtitle, *vol, *iss, *sn, *en, *ar;
+	str fulltitle, *title, *subtitle, *vol, *iss, *sn, *en, *ar;
+	int fstatus, output = 0;
 
-	fulltitle = fields_findv( f, level, FIELDS_STRP, full );
-	subtitle  = fields_findv( f, level, FIELDS_STRP, sub );
+	str_init( &fulltitle );
 
-	if ( fulltitle && fulltitle->len ) {
+	title     = fields_findv( in, level, FIELDS_STRP, ttl );
+	subtitle  = fields_findv( in, level, FIELDS_STRP, sub );
 
-		fprintf( fp, "%s %s", adstag, fulltitle->data );
-		if ( subtitle && subtitle->len ) {
-			if ( fulltitle->data[ fulltitle->len - 1 ] != '?' )
-				fprintf( fp, ": " );
-			else fprintf( fp, " " );
-			fprintf( fp, "%s", subtitle->data );
-		}
+	if ( str_has_value( title ) ) {
 
-		vol = fields_findv( f, LEVEL_ANY, FIELDS_STRP, "VOLUME" );
-		if ( vol && vol->len ) fprintf( fp, ", vol. %s", vol->data );
+		output = 1;
 
-		iss = fields_findv_firstof( f, LEVEL_ANY, FIELDS_STRP, "ISSUE",
+		title_combine( &fulltitle, title, subtitle );
+
+		vol = fields_findv( in, LEVEL_ANY, FIELDS_STRP, "VOLUME" );
+		if ( str_has_value( vol ) ) {
+			str_strcatc( &fulltitle, ", vol. " );
+			str_strcat( &fulltitle, vol );
+		}
+
+		iss = fields_findv_firstof( in, LEVEL_ANY, FIELDS_STRP, "ISSUE",
 			"NUMBER", NULL );
-		if ( iss && iss->len ) fprintf( fp, ", no. %s", iss->data );
+		if ( str_has_value( iss ) ) {
+			str_strcatc( &fulltitle, ", no. " );
+			str_strcat( &fulltitle, iss );
+		}
 
-		sn = fields_findv( f, LEVEL_ANY, FIELDS_STRP, "PAGESTART" );
-		en = fields_findv( f, LEVEL_ANY, FIELDS_STRP, "PAGEEND" );
-		ar = fields_findv( f, LEVEL_ANY, FIELDS_STRP, "ARTICLENUMBER" );
-		if ( sn && sn->len ) {
-			if ( en && en->len )
-				fprintf( fp, ", pp." );
-			else
-				fprintf( fp, ", p." );
-			fprintf( fp, " %s", sn->data );
-		} else if ( ar && ar->len ) {
-			fprintf( fp, ", p. %s", ar->data );
+		sn = fields_findv( in, LEVEL_ANY, FIELDS_STRP, "PAGES:START" );
+		en = fields_findv( in, LEVEL_ANY, FIELDS_STRP, "PAGES:STOP" );
+		ar = fields_findv( in, LEVEL_ANY, FIELDS_STRP, "ARTICLENUMBER" );
+
+		if ( str_has_value( sn ) ) {
+			if ( str_has_value( en ) ) {
+				str_strcatc( &fulltitle, ", pp. " );
+			} else {
+				str_strcatc( &fulltitle, ", p. " );
+			}
+			str_strcat( &fulltitle, sn );
+		} else if ( str_has_value( ar ) ) {
+			str_strcatc( &fulltitle, ", p. " );
+			str_strcat( &fulltitle, ar );
 		}
-		if ( en && en->len ) {
-			fprintf( fp, "-%s", en->data );
+		if ( str_has_value( en ) ) {
+			str_addchar( &fulltitle, '-' );
+			str_strcat( &fulltitle, en );
 		}
 
-		fprintf( fp, "\n" );
+		if ( str_memerr( &fulltitle ) ) {
+			*status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+
+		fstatus = fields_add( out, adstag, str_cstr( &fulltitle ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+
 	}
+
+out:
+	str_free( &fulltitle );
+
+	return output;
 }
 
 static void
-output_people( FILE *fp, fields *f, char *tag1, char *tag2, char *tag3, char *adstag, int level )
+append_titles( fields *in, int type, fields *out, int *status )
 {
-	newstr oneperson;
+	int added;
+	if ( type==TYPE_ARTICLE || type==TYPE_MAGARTICLE ) {
+		added = append_title( in, "TITLE", "SUBTITLE", "%J", LEVEL_HOST, out, status );
+		if ( added==0 )
+			(void) append_title( in, "SHORTTITLE", "SHORTSUBTITLE", "%J", LEVEL_HOST, out, status );
+	}
+}
+
+static void
+append_people( fields *in, char *tag1, char *tag2, char *tag3, char *adstag, int level, fields *out, int *status )
+{
+	str oneperson, allpeople;
+	vplist_index i;
+	int fstatus;
 	vplist a;
-	int i;
-	newstr_init( &oneperson );
+
+	str_init( &oneperson );
+	str_init( &allpeople );
 	vplist_init( &a );
-	fields_findv_eachof( f, level, FIELDS_CHRP, &a, tag1, tag2, tag3, NULL );
-	extern void  fields_findv_eachof( fields *f, int level, int mode, vplist *a, ... );
-	for ( i=0; i<a.n; ++i ) {
-		if ( i==0 ) fprintf( fp, "%s ", adstag );
-		else fprintf( fp, "; " );
-		name_build_withcomma( &oneperson, (char *) vplist_get( &a, i) );
-		fprintf( fp, "%s", oneperson.data );
+
+	fields_findv_eachof( in, level, FIELDS_CHRP, &a, tag1, tag2, tag3, NULL );
+	if ( a.n ) {
+		for ( i=0; i<a.n; ++i ) {
+			if ( i!=0 ) str_strcatc( &allpeople, "; " );
+			name_build_withcomma( &oneperson, (char *) vplist_get( &a, i) );
+			str_strcat( &allpeople, &oneperson );
+		}
+		fstatus = fields_add( out, adstag, str_cstr( &allpeople ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	}
-	if ( a.n ) fprintf( fp, "\n" );
+
 	vplist_free( &a );
-	newstr_free( &oneperson );
+	str_free( &oneperson );
+	str_free( &allpeople );
 }
 
 static void
-output_pages( FILE *fp, fields *f )
+append_pages( fields *in, fields *out, int *status )
 {
-	newstr *sn = fields_findv( f, LEVEL_ANY, FIELDS_STRP, "PAGESTART" );
-	newstr *en = fields_findv( f, LEVEL_ANY, FIELDS_STRP, "PAGEEND" );
-	newstr *ar = fields_findv( f, LEVEL_ANY, FIELDS_STRP, "ARTICLENUMBER" );
-	if ( sn && sn->len!=0 ) fprintf( fp, "%%P %s\n", sn->data );
-	else if ( ar && ar->len!=0 ) fprintf( fp, "%%P %s\n", ar->data );
-	if ( en && en->len!=0 ) fprintf( fp, "%%L %s\n", en->data );
+	str *sn, *en, *ar;
+	int fstatus;
+
+	sn = fields_findv( in, LEVEL_ANY, FIELDS_STRP, "PAGES:START" );
+	en = fields_findv( in, LEVEL_ANY, FIELDS_STRP, "PAGES:STOP" );
+	ar = fields_findv( in, LEVEL_ANY, FIELDS_STRP, "ARTICLENUMBER" );
+
+	if ( str_has_value( sn ) ) {
+		fstatus = fields_add( out, "%P", str_cstr( sn ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			*status = BIBL_ERR_MEMERR;
+			return;
+		}
+	}
+
+	else if ( str_has_value( ar ) ) {
+		fstatus = fields_add( out, "%P", str_cstr( ar ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			*status = BIBL_ERR_MEMERR;
+			return;
+		}
+	}
+
+	if ( str_has_value( en ) ) {
+		fstatus = fields_add( out, "%L", str_cstr( en ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			*status = BIBL_ERR_MEMERR;
+			return;
+		}
+	}
 }
 
 static int
@@ -240,23 +308,28 @@
 }
 
 static int
-get_month( fields *f, int level )
+get_month( fields *in, int level )
 {
-	newstr *month = fields_findv_firstof( f, level, FIELDS_STRP,
-			"MONTH", "PARTMONTH", NULL );
-	if ( month && month->len ) return mont2mont( month->data );
+	str *month;
+
+	month = fields_findv_firstof( in, level, FIELDS_STRP, "DATE:MONTH", "PARTDATE:MONTH", NULL );
+	if ( str_has_value( month ) ) return mont2mont( month->data );
 	else return 0;
 }
 
 static void
-output_date( FILE *fp, fields *f, char *adstag, int level )
+append_date( fields *in, char *adstag, int level, fields *out, int *status )
 {
-	newstr *year = fields_findv_firstof( f, level, FIELDS_STRP,
-		"YEAR", "PARTYEAR", NULL );
-	int month;
-	if ( year && year->len ) {
-		month = get_month( f, level );
-		fprintf( fp, "%s %02d/%s\n", adstag, month, year->data );
+	int month, fstatus;
+	char outstr[1000];
+	str *year;
+
+	year = fields_findv_firstof( in, level, FIELDS_STRP, "DATE:YEAR", "PARTDATE:YEAR", NULL );
+	if ( str_has_value( year ) ) {
+		month = get_month( in, level );
+		sprintf( outstr, "%02d/%s", month, str_cstr( year ) );
+		fstatus = fields_add( out, adstag, outstr, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	}
 }
 
@@ -279,29 +352,29 @@
 }
 
 static char
-get_firstinitial( fields *f )
+get_firstinitial( fields *in )
 {
 	char *name;
 	int n;
 
-	n = fields_find( f, "AUTHOR", LEVEL_MAIN );
-	if ( n==-1 ) n = fields_find( f, "AUTHOR", LEVEL_ANY );
+	n = fields_find( in, "AUTHOR", LEVEL_MAIN );
+	if ( n==-1 ) n = fields_find( in, "AUTHOR", LEVEL_ANY );
 
 	if ( n!=-1 ) {
-		name = fields_value( f, n, FIELDS_CHRP );
+		name = fields_value( in, n, FIELDS_CHRP );
 		return name[0];
 	} else return '\0';
 }
 
 static int
-get_journalabbr( fields *f )
+get_journalabbr( fields *in )
 {
 	char *jrnl;
 	int n, j;
 
-	n = fields_find( f, "TITLE", LEVEL_HOST );
+	n = fields_find( in, "TITLE", LEVEL_HOST );
 	if ( n!=-1 ) {
-		jrnl = fields_value( f, n, FIELDS_CHRP );
+		jrnl = fields_value( in, n, FIELDS_CHRP );
 		for ( j=0; j<njournals; j++ ) {
 			if ( !strcasecmp( jrnl, journals[j]+6 ) )
 				return j;
@@ -311,121 +384,211 @@
 }
 
 static void
-output_Rtag( FILE *fp, fields *f, char *adstag, int type )
+append_Rtag( fields *in, char *adstag, int type, fields *out, int *status )
 {
-	char out[20], ch;
-	int n, i;
+	char outstr[20], ch;
+	int n, i, fstatus;
 	long long page;
 
-	strcpy( out, "..................." );
+	strcpy( outstr, "..................." );
 
 	/** YYYY */
-	n = fields_find( f, "YEAR", LEVEL_ANY );
-	if ( n==-1 ) n = fields_find( f, "PARTYEAR", LEVEL_ANY );
-	if ( n!=-1 ) output_4digit_value( out, atoi( fields_value( f, n, FIELDS_CHRP ) ) );
+	n = fields_find( in, "DATE:YEAR", LEVEL_ANY );
+	if ( n==-1 ) n = fields_find( in, "PARTDATE:YEAR", LEVEL_ANY );
+	if ( n!=-1 ) output_4digit_value( outstr, atoi( fields_value( in, n, FIELDS_CHRP ) ) );
 
 	/** JJJJ */
-	n = get_journalabbr( f );
+	n = get_journalabbr( in );
 	if ( n!=-1 ) {
 		i = 0;
 		while ( i<5 && journals[n][i]!=' ' && journals[n][i]!='\t' ) {
-			out[4+i] = journals[n][i];
+			outstr[4+i] = journals[n][i];
 			i++;
 		}
 	}
 
 	/** VVVV */
-	n = fields_find( f, "VOLUME", LEVEL_ANY );
-	if ( n!=-1 ) output_4digit_value( out+9, atoi( fields_value( f, n, FIELDS_CHRP ) ) );
+	n = fields_find( in, "VOLUME", LEVEL_ANY );
+	if ( n!=-1 ) output_4digit_value( outstr+9, atoi( fields_value( in, n, FIELDS_CHRP ) ) );
 
 	/** MPPPP */
-	n = fields_find( f, "PAGESTART", LEVEL_ANY );
-	if ( n==-1 ) n = fields_find( f, "ARTICLENUMBER", LEVEL_ANY );
+	n = fields_find( in, "PAGES:START", LEVEL_ANY );
+	if ( n==-1 ) n = fields_find( in, "ARTICLENUMBER", LEVEL_ANY );
 	if ( n!=-1 ) {
-		page = atoll( fields_value( f, n, FIELDS_CHRP ) );
-		output_4digit_value( out+14, page );
+		page = atoll( fields_value( in, n, FIELDS_CHRP ) );
+		output_4digit_value( outstr+14, page );
 		if ( page>=10000 ) {
 			ch = 'a' + (page/10000);
-			out[13] = ch;
+			outstr[13] = ch;
 		}
 	}
 
 	/** A */
-        ch = toupper( (unsigned char) get_firstinitial( f ) );
-	if ( ch!='\0' ) out[18] = ch;
+        ch = toupper( (unsigned char) get_firstinitial( in ) );
+	if ( ch!='\0' ) outstr[18] = ch;
 
-	fprintf( fp, "%s %s\n", adstag, out );
+	fstatus = fields_add( out, adstag, outstr, LEVEL_MAIN );
+	if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 }
 
 static void
-output_easyall( FILE *fp, fields *f, char *tag, char *adstag, int level )
+append_easyall( fields *in, char *tag, char *adstag, int level, fields *out, int *status )
 {
+	vplist_index i;
+	int fstatus;
 	vplist a;
-	int i;
+
 	vplist_init( &a );
-	fields_findv_each( f, level, FIELDS_CHRP, &a, tag );
-	for ( i=0; i<a.n; ++i )
-		fprintf( fp, "%s %s\n", adstag, (char *) vplist_get( &a, i ) );
+
+	fields_findv_each( in, level, FIELDS_CHRP, &a, tag );
+
+	for ( i=0; i<a.n; ++i ) {
+		fstatus = fields_add( out, adstag, (char*) vplist_get( &a, i ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			*status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+	}
+out:
 	vplist_free( &a );
 }
 
 static void
-output_easy( FILE *fp, fields *f, char *tag, char *adstag, int level )
+append_easy( fields *in, char *tag, char *adstag, int level, fields *out, int *status )
 {
-	char *value = fields_findv( f, level, FIELDS_CHRP, tag );
-	if ( value && value[0]!='\0' ) fprintf( fp, "%s %s\n", adstag, value );
+	char *value;
+	int fstatus;
+
+	value = fields_findv( in, level, FIELDS_CHRP, tag );
+	if ( value && value[0]!='\0' ) {
+		fstatus = fields_add( out, adstag, value, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 }
 
 static void
-output_keys( FILE *fp, fields *f, char *tag, char *adstag, int level )
+append_keys( fields *in, char *tag, char *adstag, int level, fields *out, int *status )
 {
+	vplist_index i;
+	str allkeys;
+	int fstatus;
 	vplist a;
-	int i;
+
+	str_init( &allkeys );
 	vplist_init( &a );
-	fields_findv_each( f, level, FIELDS_CHRP, &a, tag );
-	for ( i=0; i<a.n; ++i ) {
-		if ( i==0 ) fprintf( fp, "%s ", adstag );
-		else fprintf( fp, ", " );
-		fprintf( fp, "%s", (char *) vplist_get( &a, i ) );
+
+	fields_findv_each( in, level, FIELDS_CHRP, &a, tag );
+
+	if ( a.n ) {
+		for ( i=0; i<a.n; ++i ) {
+			if ( i>0 ) str_strcatc( &allkeys, ", " );
+			str_strcatc( &allkeys, (char *) vplist_get( &a, i ) );
+		}
+		fstatus = fields_add( out, adstag, str_cstr( &allkeys ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	}
-	if ( a.n ) fprintf( fp, "\n" );
+
+	str_free( &allkeys );
 	vplist_free( &a );
 }
 
-void
-adsout_write( fields *f, FILE *fp, param *p, unsigned long refnum )
+static void
+append_urls( fields *in, fields *out, int *status )
 {
-	int type;
-	fields_clearused( f );
-	type = get_type( f );
+	int lstatus;
+	slist types;
 
-	output_people(  fp, f, "AUTHOR", "AUTHOR:ASIS", "AUTHOR:CORP", "%A", LEVEL_MAIN );
-	output_people(  fp, f, "EDITOR", "EDITOR:ASIS", "EDITOR:CORP", "%E", LEVEL_ANY );
-	output_easy(    fp, f, "TITLE",       "%T", LEVEL_ANY );
+	lstatus = slist_init_valuesc( &types, "URL", "DOI", "PMID", "PMC", "ARXIV", "JSTOR", "MRNUMBER", "FILEATTACH", "FIGATTACH", NULL );
+	if ( lstatus!=SLIST_OK ) {
+		*status = BIBL_ERR_MEMERR;
+		return;
+	}
 
-	if ( type==TYPE_ARTICLE || type==TYPE_MAGARTICLE )
-		output_title( fp, f, "TITLE", "SUBTITLE", "%J", LEVEL_HOST );
+	*status = urls_merge_and_add( in, LEVEL_ANY, out, "%U", LEVEL_MAIN, &types );
 
-	output_date(    fp, f,               "%D", LEVEL_ANY );
-	output_easy(    fp, f, "VOLUME",     "%V", LEVEL_ANY );
-	output_easy(    fp, f, "ISSUE",      "%N", LEVEL_ANY );
-	output_easy(    fp, f, "NUMBER",     "%N", LEVEL_ANY );
-	output_easy(    fp, f, "LANGUAGE",   "%M", LEVEL_ANY );
-	output_easyall( fp, f, "NOTES",      "%X", LEVEL_ANY );
-	output_easy(    fp, f, "ABSTRACT",   "%B", LEVEL_ANY );
-	output_keys(    fp, f, "KEYWORD",    "%K", LEVEL_ANY );
-	output_easyall( fp, f, "URL",        "%U", LEVEL_ANY ); 
-	output_easyall( fp, f, "FILEATTACH", "%U", LEVEL_ANY ); 
-	output_easyall( fp, f, "FIGATTACH",  "%U", LEVEL_ANY ); 
-	output_pages( fp, f );
-	output_easyall( fp, f, "DOI",        "%Y", LEVEL_ANY );
-        fprintf( fp, "%%W PHY\n%%G AUTHOR\n" );
-	output_Rtag( fp, f, "%R", type );
+	slist_free( &types );
+}
+
+static void
+append_trailer( fields *out, int *status )
+{
+	int fstatus;
+
+	fstatus = fields_add( out, "%W", "PHY", LEVEL_MAIN );
+	if ( fstatus!=FIELDS_OK ) {
+		*status = BIBL_ERR_MEMERR;
+		return;
+	}
+
+	fstatus = fields_add( out, "%G", "AUTHOR", LEVEL_MAIN );
+	if ( fstatus!=FIELDS_OK ) {
+		*status = BIBL_ERR_MEMERR;
+		return;
+	}
+}
+
+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 );
 }
 
-void
+static int
+append_data( fields *in, fields *out )
+{
+	int type, status = BIBL_OK;
+
+	fields_clearused( in );
+	type = get_type( in );
+
+	append_people ( in, "AUTHOR", "AUTHOR:ASIS", "AUTHOR:CORP", "%A", LEVEL_MAIN, out, &status );
+	append_people ( in, "EDITOR", "EDITOR:ASIS", "EDITOR:CORP", "%E", LEVEL_ANY,  out, &status );
+	append_easy   ( in, "TITLE",       "%T", LEVEL_ANY, out, &status );
+	append_titles ( in, type, out, &status );
+	append_date   ( in,               "%D", LEVEL_ANY, out, &status );
+	append_easy   ( in, "VOLUME",     "%V", LEVEL_ANY, out, &status );
+	append_easy   ( in, "ISSUE",      "%N", LEVEL_ANY, out, &status );
+	append_easy   ( in, "NUMBER",     "%N", LEVEL_ANY, out, &status );
+	append_easy   ( in, "LANGUAGE",   "%M", LEVEL_ANY, out, &status );
+	append_easyall( in, "NOTES",      "%X", LEVEL_ANY, out, &status );
+	append_easy   ( in, "ABSTRACT",   "%B", LEVEL_ANY, out, &status );
+	append_keys   ( in, "KEYWORD",    "%K", LEVEL_ANY, out, &status );
+	append_urls   ( in, out, &status );
+	append_pages  ( in, out, &status );
+	append_easyall( in, "DOI",        "%Y", LEVEL_ANY, out, &status );
+	append_trailer( out, &status );
+	append_Rtag   ( in, "%R", type, out, &status );
+
+	return status;
+}
+
+static int
+adsout_write( fields *in, 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 );
+
+	return status;
+}
+
+static void
 adsout_writeheader( FILE *outptr, param *p )
 {
 	if ( p->utf8bom ) utf8_writebom( outptr );
diff --git a/bibutils/adsout.h b/bibutils/adsout.h
deleted file mode 100644
--- a/bibutils/adsout.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * adsout.h
- *
- * Copyright (c) Richard Mathar 2007-2013
- * Copyright (c) Chris Putnam 2007-2013
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef ADSOUT_H
-#define ADSOUT_H
-
-#include <stdio.h>
-#include "bibutils.h"
-
-extern void adsout_write( fields *info, FILE *fp, param *p,
-		unsigned long refnum );
-extern void adsout_writeheader( FILE *outptr, param *p );
-
-extern void adsout_initparams( param *p, const char *progname );
-
-#endif
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-2014
+ * Copyright (c) Chris Putnam 2005-2017
  *
  * Source code released under the GPL version 2
  *
@@ -13,7 +13,7 @@
 /* internal includes */
 #include "reftypes.h"
 #include "charsets.h"
-#include "newstr_conv.h"
+#include "str_conv.h"
 #include "is_ws.h"
 
 /* illegal modes to pass in, but use internally for consistency */
@@ -95,13 +95,13 @@
 static int
 bibl_duplicateparams( param *np, param *op )
 {
-	int ok;
-	list_init( &(np->asis) );
-	list_init( &(np->corps) );
-	ok = list_copy( &(np->asis), &(op->asis ) );
-	if ( !ok ) return BIBL_ERR_MEMERR;
-	ok = list_copy( &(np->corps), &(op->corps ) );
-	if ( !ok ) return BIBL_ERR_MEMERR;
+	int status;
+	slist_init( &(np->asis) );
+	slist_init( &(np->corps) );
+	status = slist_copy( &(np->asis), &(op->asis ) );
+	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 {
@@ -190,8 +190,8 @@
 bibl_freeparams( param *p )
 {
 	if ( p ) {
-		list_free( &(p->asis) );
-		list_free( &(p->corps) );
+		slist_free( &(p->asis) );
+		slist_free( &(p->corps) );
 		if ( p->progname ) free( p->progname );
 	}
 }
@@ -204,10 +204,10 @@
 	if ( !p ) return BIBL_ERR_BADINPUT;
 	if ( !f ) return BIBL_ERR_BADINPUT;
 
-	status = list_fill( &(p->asis), f, 1 );
+	status = slist_fill( &(p->asis), f, 1 );
 
-	if ( status == LIST_ERR_CANNOTOPEN ) return BIBL_ERR_CANTOPEN;
-	else if ( status == 0 ) return BIBL_ERR_MEMERR;
+	if ( status == SLIST_ERR_CANTOPEN ) return BIBL_ERR_CANTOPEN;
+	else if ( status == SLIST_ERR_MEMERR ) return BIBL_ERR_MEMERR;
 	return BIBL_OK;
 }
 
@@ -219,9 +219,9 @@
 	if ( !p ) return BIBL_ERR_BADINPUT;
 	if ( !f ) return BIBL_ERR_BADINPUT;
 
-	status = list_fill( &(p->corps), f, 1 );
+	status = slist_fill( &(p->corps), f, 1 );
 
-	if ( status == LIST_ERR_CANNOTOPEN ) return BIBL_ERR_CANTOPEN;
+	if ( status == SLIST_ERR_CANTOPEN ) return BIBL_ERR_CANTOPEN;
 	else if ( status == 0 ) return BIBL_ERR_MEMERR;
 	return BIBL_OK;
 }
@@ -233,12 +233,12 @@
 int
 bibl_addtoasis( param *p, char *d )
 {
-	newstr *s;
+	str *s;
 
 	if ( !p ) return BIBL_ERR_BADINPUT;
 	if ( !d ) return BIBL_ERR_BADINPUT;
 
-	s = list_addc( &(p->asis), d );
+	s = slist_addc( &(p->asis), d );
 
 	return ( s==NULL )? BIBL_ERR_MEMERR : BIBL_OK;
 }
@@ -250,12 +250,12 @@
 int
 bibl_addtocorps( param *p, char *d )
 {
-	newstr *s;
+	str *s;
 
 	if ( !p ) return BIBL_ERR_BADINPUT;
 	if ( !d ) return BIBL_ERR_BADINPUT;
 
-	s = list_addc( &(p->corps), d );
+	s = slist_addc( &(p->corps), d );
 
 	return ( s==NULL )? BIBL_ERR_MEMERR : BIBL_OK;
 }
@@ -343,23 +343,23 @@
  * value = "Masters thesis"
  */
 static int
-extract_tag_value( newstr *tag, newstr *value, char *p )
+extract_tag_value( str *tag, str *value, char *p )
 {
-	newstr_empty( tag );
+	str_empty( tag );
 	while ( p && *p && *p!='|' ) {
-		newstr_addchar( tag, *p );
+		str_addchar( tag, *p );
 		p++;
 	}
-	if ( newstr_memerr( tag ) ) return BIBL_ERR_MEMERR;
+	if ( str_memerr( tag ) ) return BIBL_ERR_MEMERR;
 
 	if ( p && *p=='|' ) p++;
 
-	newstr_empty( value );
+	str_empty( value );
 	while ( p && *p ) {
-		newstr_addchar( value, *p );
+		str_addchar( value, *p );
 		p++;
 	}
-	if ( newstr_memerr( tag ) ) return BIBL_ERR_MEMERR;
+	if ( str_memerr( tag ) ) return BIBL_ERR_MEMERR;
 
 	return BIBL_OK;
 }
@@ -374,10 +374,10 @@
 process_defaultadd( fields *f, int reftype, param *r )
 {
 	int i, n, process, level, status, ret = BIBL_OK;
-	newstr tag, value;
+	str tag, value;
 	char *p;
 
-	newstrs_init( &tag, &value, NULL );
+	strs_init( &tag, &value, NULL );
 
 	for ( i=0; i<r->all[reftype].ntags; ++i ) {
 
@@ -404,7 +404,7 @@
 
 	}
 out:
-	newstrs_free( &tag, &value, NULL );
+	strs_free( &tag, &value, NULL );
 
 	return ret;
 }
@@ -419,10 +419,10 @@
 process_alwaysadd( fields *f, int reftype, param *r )
 {
 	int i, process, level, status, ret = BIBL_OK;
-	newstr tag, value;
+	str tag, value;
 	char *p;
 
-	newstrs_init( &tag, &value, NULL );
+	strs_init( &tag, &value, NULL );
 
 	for ( i=0; i<r->all[reftype].ntags; ++i ) {
 
@@ -446,7 +446,7 @@
 	}
 
 out:
-	newstrs_free( &tag, &value, NULL );
+	strs_free( &tag, &value, NULL );
 
 	return ret;
 }
@@ -455,11 +455,11 @@
 read_ref( FILE *fp, bibl *bin, char *filename, param *p )
 {
 	int nrefs = 0, bufpos = 0, ok, ret=BIBL_OK, fcharset;/* = CHARSET_UNKNOWN;*/
-	newstr reference, line;
+	str reference, line;
 	char buf[256]="";
 	fields *ref;
-	newstr_init( &reference );
-	newstr_init( &line );
+	str_init( &reference );
+	str_init( &line );
 	while ( p->readf( fp, buf, sizeof(buf), &bufpos, &line, &reference, &fcharset ) ) {
 		if ( reference.len==0 ) continue;
 		ref = fields_new();
@@ -468,7 +468,7 @@
 			bibl_free( bin );
 			goto out;
 		}
-		if ( p->processf( ref, reference.data, filename, nrefs+1 )){
+		if ( p->processf( ref, reference.data, filename, nrefs+1, p )){
 			ok = bibl_addref( bin, ref );
 			if ( !ok ) {
 				ret = BIBL_ERR_MEMERR;
@@ -481,7 +481,7 @@
 			fields_free( ref );
 			free( ref );
 		}
-		newstr_empty( &reference );
+		str_empty( &reference );
 		if ( fcharset!=CHARSET_UNKNOWN ) {
 			/* charset from file takes priority over default, but
 			 * not user-specified */
@@ -494,8 +494,8 @@
 	}
 	if ( p->charsetin==CHARSET_UNICODE ) p->utf8in = 1;
 out:
-	newstr_free( &line );
-	newstr_free( &reference );
+	str_free( &line );
+	str_free( &reference );
 	return ret;
 }
 
@@ -517,7 +517,7 @@
 static int
 bibl_fixcharsetdata( fields *ref, param *p )
 {
-	newstr *data;
+	str *data;
 	char *tag;
 	long i, n;
 	int ok;
@@ -530,11 +530,11 @@
 		data = fields_value( ref, i, FIELDS_STRP_NOUSE );
 
 		if ( bibl_notexify( tag ) ) {
-			ok = newstr_convert( data,
+			ok = str_convert( data,
 				p->charsetin,  0, p->utf8in,  p->xmlin,
 				p->charsetout, 0, p->utf8out, p->xmlout );
 		} else {
-			ok = newstr_convert( data,
+			ok = str_convert( data,
 				p->charsetin,  p->latexin,  p->utf8in,  p->xmlin,
 				p->charsetout, p->latexout, p->utf8out, p->xmlout );
 		}
@@ -564,16 +564,16 @@
 {
 	char *year, *author, *p, num[512];
 	int status, ret = BIBL_OK;
-	newstr refnum;
+	str refnum;
 
 	*n = -1;
 
-	newstr_init( &refnum );
+	str_init( &refnum );
 
-	year = fields_findv( f, LEVEL_MAIN, FIELDS_CHRP_NOUSE, "YEAR" );
+	year = fields_findv( f, LEVEL_MAIN, FIELDS_CHRP_NOUSE, "DATE:YEAR" );
 	if ( !year )
 		year = fields_findv_firstof( f, LEVEL_ANY, FIELDS_CHRP_NOUSE,
-			"YEAR", "PARTYEAR", NULL );
+			"DATE:YEAR", "PARTDATE:YEAR", NULL );
 
 	author = fields_findv( f, LEVEL_MAIN, FIELDS_CHRP_NOUSE, "AUTHOR" );
 	if ( !author )
@@ -583,15 +583,15 @@
 	if ( year && author ) {
 		p = author;
 		while ( *p && *p!='|' )
-			newstr_addchar( &refnum, *p++ );
+			str_addchar( &refnum, *p++ );
 		p = year;
 		while ( *p && *p!=' ' && *p!='\t' )
-			newstr_addchar( &refnum, *p++ );
+			str_addchar( &refnum, *p++ );
 	} else {
 		sprintf( num, "%ld", nrefs );
-		newstr_mergestrs( &refnum, "ref", num, NULL );
+		str_mergestrs( &refnum, "ref", num, NULL );
 	}
-	if ( newstr_memerr( &refnum ) ) {
+	if ( str_memerr( &refnum ) ) {
 		ret = BIBL_ERR_MEMERR;
 		goto out;
 	}
@@ -601,7 +601,7 @@
 	else *n = fields_find( f, "REFNUM", 0 );
 
 out:
-	newstr_free( &refnum );
+	str_free( &refnum );
 
 	return ret;
 }
@@ -623,8 +623,8 @@
 		}
 		if ( p->addcount ) {
 			sprintf( buf, "_%ld", i+1 );
-			newstr_strcat( &(ref->data[n]), buf );
-			if ( newstr_memerr( &(ref->data[n]) ) )
+			str_strcatc( &(ref->data[n]), buf );
+			if ( str_memerr( &(ref->data[n]) ) )
 				return BIBL_ERR_MEMERR;
 		}
 	}
@@ -637,28 +637,28 @@
 {
 	int n1, n2, status, ret;
 	char *p, buf[100];
-	newstr citekey;
+	str citekey;
 
-	newstr_init( &citekey );
+	str_init( &citekey );
 
 	n1 = fields_find( f, "AUTHOR", 0 );
 	if ( n1==-1 ) n1 = fields_find( f, "AUTHOR", -1 );
-	n2 = fields_find( f, "YEAR", 0 );
-	if ( n2==-1 ) n2 = fields_find( f, "YEAR", -1 );
-	if ( n2==-1 ) n2 = fields_find( f, "PARTYEAR", 0 );
-	if ( n2==-1 ) n2 = fields_find( f, "PARTYEAR", -1 );
+	n2 = fields_find( f, "DATE:YEAR", 0 );
+	if ( n2==-1 ) n2 = fields_find( f, "DATE:YEAR", -1 );
+	if ( n2==-1 ) n2 = fields_find( f, "PARTDATE:YEAR", 0 );
+	if ( n2==-1 ) n2 = fields_find( f, "PARTDATE:YEAR", -1 );
 	if ( n1!=-1 && n2!=-1 ) {
 		p = f->data[n1].data;
 		while ( p && *p && *p!='|' ) {
-			if ( !is_ws( *p ) ) newstr_addchar( &citekey, *p ); 
+			if ( !is_ws( *p ) ) str_addchar( &citekey, *p ); 
 			p++;
 		}
 		p = f->data[n2].data;
 		while ( p && *p ) {
-			if ( !is_ws( *p ) ) newstr_addchar( &citekey, *p );
+			if ( !is_ws( *p ) ) str_addchar( &citekey, *p );
 			p++;
 		}
-		if ( newstr_memerr( &citekey ) ) {
+		if ( str_memerr( &citekey ) ) {
 			ret = -1;
 			goto out;
 		}
@@ -669,41 +669,41 @@
 		}
 	} else {
 		sprintf( buf, "ref%d\n", nref );
-		newstr_strcpy( &citekey, buf );
+		str_strcpyc( &citekey, buf );
 	}
 	ret = fields_find( f, "REFNUM", -1 );
 out:
-	newstr_free( &citekey );
+	str_free( &citekey );
 	return ret;
 }
 
 static int
-resolve_citekeys( bibl *b, list *citekeys, int *dup )
+resolve_citekeys( bibl *b, slist *citekeys, int *dup )
 {
 	const char abc[]="abcdefghijklmnopqrstuvwxyz";
 	int nsame, ntmp, n, i, j, status = BIBL_OK;
-	newstr tmp;
+	str tmp;
 
-	newstr_init( &tmp );
+	str_init( &tmp );
 
 	for ( i=0; i<citekeys->n; ++i ) {
 		if ( dup[i]==-1 ) continue;
 		nsame = 0;
 		for ( j=i; j<citekeys->n; ++j ) {
 			if ( dup[j]!=i ) continue;
-			newstr_newstrcpy( &tmp, &(citekeys->str[j]) );
-			if ( newstr_memerr( &tmp ) ) {
+			str_strcpy( &tmp, slist_str( citekeys, j ) );
+			if ( str_memerr( &tmp ) ) {
 				status = BIBL_ERR_MEMERR;
 				goto out;
 			}
 			ntmp = nsame;
 			while ( ntmp >= 26 ) {
-				newstr_addchar( &tmp, 'a' );
+				str_addchar( &tmp, 'a' );
 					ntmp -= 26;
 			}
 			if ( ntmp<26 && ntmp>=0 )
-			newstr_addchar( &tmp, abc[ntmp] );
-			if ( newstr_memerr( &tmp ) ) {
+			str_addchar( &tmp, abc[ntmp] );
+			if ( str_memerr( &tmp ) ) {
 				status = BIBL_ERR_MEMERR;
 				goto out;
 			}
@@ -711,8 +711,8 @@
 			dup[j] = -1;
 			n = fields_find( b->ref[j], "REFNUM", -1 );
 			if ( n!=-1 ) {
-				newstr_newstrcpy(&((b->ref[j])->data[n]),&tmp);
-				if ( newstr_memerr( &((b->ref[j])->data[n]) ) ) {
+				str_strcpy(&((b->ref[j])->data[n]),&tmp);
+				if ( str_memerr( &((b->ref[j])->data[n]) ) ) {
 					status = BIBL_ERR_MEMERR;
 					goto out;
 				}
@@ -720,25 +720,25 @@
 		}
 	}
 out:
-	newstr_free( &tmp );
+	str_free( &tmp );
 	return status;
 }
 
 static int
-get_citekeys( bibl *b, list *citekeys )
+get_citekeys( bibl *b, slist *citekeys )
 {
-	newstr *s;
 	fields *f;
 	int i, n;
+	str *s;
 	for ( i=0; i<b->nrefs; ++i ) {
 		f = b->ref[i];
 		n = fields_find( f, "REFNUM", -1 );
 		if ( n==-1 ) n = generate_citekey( f, i );
 		if ( n!=-1 && f->data[n].data ) {
-			s = list_add( citekeys, &(f->data[n]) );
+			s = slist_add( citekeys, &(f->data[n]) );
 			if ( !s ) return BIBL_ERR_MEMERR;
 		} else {
-			s = list_addc( citekeys, "" );
+			s = slist_addc( citekeys, "" );
 			if ( !s ) return BIBL_ERR_MEMERR;
 		}
 	}
@@ -746,7 +746,7 @@
 }
 
 static int 
-dup_citekeys( bibl *b, list *citekeys )
+dup_citekeys( bibl *b, slist *citekeys )
 {
 	int i, j, status = BIBL_OK, *dup, ndup=0;
 
@@ -757,8 +757,8 @@
 	for ( i=0; i<citekeys->n-1; ++i ) {
 		if ( dup[i]!=-1 ) continue;
 		for ( j=i+1; j<citekeys->n; ++j ) {
-			if ( !strcmp( citekeys->str[i].data, 
-				citekeys->str[j].data ) ) {
+			if ( !strcmp( slist_cstr( citekeys, i ),
+			              slist_cstr( citekeys, j ) ) ) {
 					dup[i] = i;
 					dup[j] = i;
 					ndup++;
@@ -773,14 +773,14 @@
 static int
 uniqueify_citekeys( bibl *b )
 {
-	list citekeys;
+	slist citekeys;
 	int status;
-	list_init( &citekeys );
+	slist_init( &citekeys );
 	status = get_citekeys( b, &citekeys );
 	if ( status!=BIBL_OK ) goto out;
 	status = dup_citekeys( b, &citekeys );
 out:
-	list_free( &citekeys );
+	slist_free( &citekeys );
 	return status;
 }
 
@@ -803,8 +803,8 @@
 		rout = fields_new();
 		if ( !rout ) return BIBL_ERR_MEMERR;
 		if ( p->typef ) 
-			reftype = p->typef( rin, fname, i+1, p, p->all, p->nall );
-		status = p->convertf( rin, rout, reftype, p, p->all, p->nall );
+			reftype = p->typef( rin, fname, i+1, p );
+		status = p->convertf( rin, rout, reftype, p );
 		if ( status!=BIBL_OK ) return status;
 		if ( p->all ) {
 			status = process_alwaysadd( rout, reftype, p );
@@ -815,6 +815,13 @@
 		ok = bibl_addref( bout, rout );
 		if ( !ok ) return BIBL_ERR_MEMERR;
 	}
+	if ( debug_set( p ) ) {
+		fflush( stdout );
+		fprintf( stderr, "-------------------start for convert_ref\n");
+		bibl_verbose0( bout );
+		fprintf( stderr, "-------------------end for convert_ref\n" );
+		fflush( stderr );
+	}
 	status = uniqueify_citekeys( bout );
 	return status;
 }
@@ -838,7 +845,10 @@
 	bibl_init( &bin );
 
 	status = read_ref( fp, &bin, filename, &lp );
-	if ( status!=BIBL_OK ) return status;
+	if ( status!=BIBL_OK ) {
+		bibl_freeparams( &lp );
+		return status;
+	}
 
 	if ( debug_set( p ) ) {
 		fflush( stdout );
@@ -884,13 +894,18 @@
 			fflush( stderr );
 		}
 		ok = bibl_copy( b, &bin );
-		if ( !ok ) return BIBL_ERR_MEMERR;
+		if ( !ok ) {
+			bibl_freeparams( &lp );
+			return BIBL_ERR_MEMERR;
+		}
 	}
 	if ( !lp.output_raw || ( lp.output_raw & BIBL_RAW_WITHMAKEREFID ) )
 		bibl_checkrefid( b, &lp );
 
 	bibl_free( &bin );
 
+	bibl_freeparams( &lp );
+
 	return BIBL_OK;
 }
 
@@ -932,14 +947,16 @@
 static int
 bibl_writeeachfp( FILE *fp, bibl *b, param *p )
 {
+	int status;
 	long i;
 	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 );
-		p->writef( b->ref[i], fp, p, i );
+		status = p->writef( b->ref[i], fp, p, i );
 		if ( p->footerf ) p->footerf( fp );
 		fclose( fp );
+		if ( status!=BIBL_OK ) return status;
 	}
 	return BIBL_OK;
 }
@@ -947,12 +964,15 @@
 static int
 bibl_writefp( FILE *fp, bibl *b, param *p )
 {
+	int status = BIBL_OK;
 	long i;
 	if ( p->headerf ) p->headerf( fp, p );
-	for ( i=0; i<b->nrefs; ++i )
-		p->writef( b->ref[i], fp, p, i );
+	for ( i=0; i<b->nrefs; ++i ) {
+		status = p->writef( b->ref[i], fp, p, i );
+		if ( status!=BIBL_OK ) break;
+	}
 	if ( p->footerf ) p->footerf( fp );
-	return BIBL_OK;
+	return status;
 }
 
 int
@@ -972,10 +992,19 @@
 	status = bibl_fixcharsets( b, &lp );
 	if ( status!=BIBL_OK ) return status;
 
-	if ( debug_set( p ) ) report_params( stderr, "bibl_write", &lp );
+	if ( debug_set( p ) ) {
+		report_params( stderr, "bibl_write", &lp );
+		fflush( stdout );
+		fprintf( stderr, "-------------------start for bibl_write\n");
+		bibl_verbose0( b );
+		fprintf( stderr, "-------------------end for bibl_write\n" );
+		fflush( stderr );
+	}
 
 	if ( p->singlerefperfile ) status = bibl_writeeachfp( fp, b, &lp );
 	else status = bibl_writefp( fp, b, &lp );
+
+	bibl_freeparams( &lp );
 
 	return status;
 }
diff --git a/bibutils/bibformats.h b/bibutils/bibformats.h
new file mode 100644
--- /dev/null
+++ b/bibutils/bibformats.h
@@ -0,0 +1,35 @@
+/*
+ * bibformats.h
+ *
+ * Copyright (c) Chris Putnam 2007-2017
+ *
+ * Source code released under the GPL version 2
+ *
+ */
+#ifndef BIBFORMATS_H
+#define BIBFORMATS_H
+
+#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 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 );
+
+#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-2014
+ * Copyright (c) Chris Putnam 2005-2017
  *
  * Source code released under the GPL version 2
  *
@@ -92,7 +92,7 @@
 			value = fields_value( refin, j, FIELDS_CHRP );
 			level = fields_level( refin, j );
 			if ( tag && value ) {
-				status = fields_add( refout, tag, value, level );
+				status = fields_add_can_dup( refout, tag, value, level );
 				if ( status!=FIELDS_OK ) return 0;
 			}
 		}
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-2014
+ * Copyright (c) Chris Putnam 2005-2017
  *
  * Source code released under the GPL version 2
  *
@@ -10,7 +10,7 @@
 #define BIBL_H
 
 #include <stdio.h>
-#include "newstr.h"
+#include "str.h"
 #include "fields.h"
 #include "reftypes.h"
 
diff --git a/bibutils/biblatexin.c b/bibutils/biblatexin.c
--- a/bibutils/biblatexin.c
+++ b/bibutils/biblatexin.c
@@ -1,1412 +1,1300 @@
 /*
  * biblatexin.c
  *
- * Copyright (c) Chris Putnam 2008-2014
- * Copyright (c) Johannes Wilm 2010-2014
- *
- * Program and source code released under the GPL version 2
- *
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include "is_ws.h"
-#include "strsearch.h"
-#include "newstr.h"
-#include "newstr_conv.h"
-#include "fields.h"
-#include "list.h"
-#include "name.h"
-#include "reftypes.h"
-#include "biblatexin.h"
-
-extern const char progname[];
-
-static list find    = { 0, 0, NULL, 0 };
-static list replace = { 0, 0, NULL, 0 };
-
-/*****************************************************
- PUBLIC: void biblatexin_initparams()
-*****************************************************/
-
-void
-biblatexin_initparams( param *p, 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;
-
-	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;
-
-	list_init( &(p->asis) );
-	list_init( &(p->corps) );
-
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
-}
-
-/*****************************************************
- PUBLIC: int biblatexin_readf()
-*****************************************************/
-
-/*
- * readf can "read too far", so we store this information in line, thus
- * the next new text is in line, either from having read too far or
- * from the next chunk obtained via newstr_fget()
- *
- * return 1 on success, 0 on error/end-of-file
- *
- */
-static int
-readmore( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line )
-{
-	if ( line->len ) return 1;
-	else return newstr_fget( fp, buf, bufsize, bufpos, line );
-}
-
-/*
- * readf()
- *
- * returns zero if cannot get reference and hit end of-file
- * returns 1 if last reference in file, 2 if reference within file
- */
-int
-biblatexin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset )
-{
-	int haveref = 0;
-	char *p;
-	while ( haveref!=2 && readmore( fp, buf, bufsize, bufpos, line ) ) {
-		if ( line->len == 0 ) continue; /* blank line */
-		p = &(line->data[0]);
-		p = skip_ws( p );
-		if ( *p == '%' ) { /* commented out line */
-			newstr_empty( line );
-			continue;
-		}
-		if ( *p == '@' ) haveref++;
-		if ( haveref && haveref<2 ) {
-			newstr_strcat( reference, p );
-			newstr_addchar( reference, '\n' );
-			newstr_empty( line );
-		} else if ( !haveref ) newstr_empty( line );
-	
-	}
-	*fcharset = CHARSET_UNKNOWN;
-	return haveref;
-}
-
-/*****************************************************
- PUBLIC: int biblatexin_processf()
-*****************************************************/
-
-static char *
-process_biblatextype( char *p, newstr *type )
-{
-	newstr tmp;
-	newstr_init( &tmp );
-
-	if ( *p=='@' ) p++;
-	p = newstr_cpytodelim( &tmp, p, "{( \t\r\n", 0 );
-	p = skip_ws( p );
-	if ( *p=='{' || *p=='(' ) p++;
-	p = skip_ws( p );
-
-	if ( tmp.len ) newstr_strcpy( type, tmp.data );
-	else newstr_empty( type );
-
-	newstr_free( &tmp );
-	return p;
-}
-
-static char *
-process_biblatexid( char *p, newstr *id )
-{
-	char *start_p = p;
-	newstr tmp;
-
-	newstr_init( &tmp );
-	p = newstr_cpytodelim( &tmp, p, ",", 1 );
-
-	if ( tmp.len ) {
-		if ( strchr( tmp.data, '=' ) ) {
-			/* Endnote writes biblatex files w/o fields, try to
-			 * distinguish via presence of an equal sign.... if
-			 * it's there, assume that it's a tag/data pair instead
-			 * and roll back.
-			 */
-			p = start_p;
-			newstr_empty( id );
-		} else {
-			newstr_strcpy( id, tmp.data );
-		}
-	} else {
-		newstr_empty( id );
-	}
-
-	newstr_free( &tmp );
-	return skip_ws( p );
-}
-
-static char *
-biblatex_tag( char *p, newstr *tag )
-{
-	p = newstr_cpytodelim( tag, skip_ws( p ), "= \t\r\n", 0 );
-	return skip_ws( p );
-}
-
-static char *
-biblatex_data( char *p, fields *bibin, list *tokens )
-{
-	unsigned int nbracket = 0, nquotes = 0;
-	char *startp = p;
-	newstr tok, *s;
-
-	newstr_init( &tok );
-	while ( p && *p ) {
-		if ( !nquotes && !nbracket ) {
-			if ( *p==',' || *p=='=' || *p=='}' || *p==')' )
-				goto out;
-		}
-		if ( *p=='\"' && nbracket==0 && ( p==startp || *(p-1)!='\\' ) ) {
-			nquotes = !nquotes;
-			newstr_addchar( &tok, *p );
-			if ( !nquotes ) {
-				s = list_add( tokens, &tok );
-				if ( !s ) { p = NULL; goto outerr; }
-				newstr_empty( &tok );
-			}
-		} else if ( *p=='#' && !nquotes && !nbracket ) {
-			if ( tok.len ) {
-				s = list_add( tokens, &tok );
-				if ( !s ) { p = NULL; goto outerr; }
-			}
-			newstr_strcpy( &tok, "#" );
-			s = list_add( tokens, &tok );
-			if ( !s ) { p = NULL; goto outerr; }
-			newstr_empty( &tok );
-		} else if ( *p=='{' && !nquotes && ( p==startp || *(p-1)!='\\' ) ) {
-			nbracket++;
-			newstr_addchar( &tok, *p );
-		} else if ( *p=='}' && !nquotes && ( p==startp || *(p-1)!='\\' ) ) {
-			nbracket--;
-			newstr_addchar( &tok, *p );
-			if ( nbracket==0 ) {
-				s = list_add( tokens, &tok );
-				if ( !s ) { p = NULL; goto outerr; }
-				newstr_empty( &tok );
-			}
-		} else if ( !is_ws( *p ) || nquotes || nbracket ) {
-			if ( !is_ws( *p ) ) newstr_addchar( &tok, *p );
-			else {
-				if ( tok.len!=0 && *p!='\n' && *p!='\r' )
-					newstr_addchar( &tok, *p );
-				else if ( tok.len!=0 && (*p=='\n' || *p=='\r')) {
-					newstr_addchar( &tok, ' ' );
-					while ( is_ws( *(p+1) ) ) p++;
-				}
-			}
-		} else if ( is_ws( *p ) ) {
-			if ( tok.len ) {
-				s = list_add( tokens, &tok );
-				if ( !s ) { p = NULL; goto outerr; }
-				newstr_empty( &tok );
-			}
-		}
-		p++;
-	}
-out:
-	if ( nbracket!=0 ) {
-		fprintf( stderr, "%s: Mismatch in number of brackets in reference.\n", progname );
-	}
-	if ( nquotes!=0 ) {
-		fprintf( stderr, "%s: Mismatch in number of quotes in reference.\n", progname );
-	}
-	if ( tok.len ) {
-		s = list_add( tokens, &tok );
-		if ( !s ) p = NULL;
-	}
-outerr:
-	newstr_free( &tok );
-	return p;
-}
-
-/* replace_strings()
- *
- * do string replacement -- only if unprotected by quotation marks or curly brackets
- */
-static void
-replace_strings( list *tokens, fields *bibin )
-{
-	int i, n, ok;
-	newstr *s;
-	char *q;
-	i = 0;
-	while ( i < tokens->n ) {
-		s = list_get( tokens, i );
-		if ( !strcmp( s->data, "#" ) ) {
-		} else if ( s->data[0]!='\"' && s->data[0]!='{' ) {
-			n = list_find( &find, s->data );
-			if ( n!=-1 ) {
-				newstr_newstrcpy( s, list_get( &replace, n ) );
-			} else {
-				q = s->data;
-				ok = 1;
-				while ( *q && ok ) {
-					if ( !isdigit( *q ) ) ok = 0;
-					q++;
-				}
-				if ( !ok ) {
-					fprintf( stderr, "%s: Warning: Non-numeric "
-					   "BibTeX elements should be in quotations or "
-					   "curly brackets in reference.\n", progname );
-				}
-			}
-		}
-		i++;
-	}
-}
-
-static int
-string_concatenate( list *tokens, fields *bibin )
-{
-	int i, status;
-	newstr *s, *t;
-	i = 0;
-	while ( i < tokens->n ) {
-		s = list_get( tokens, i );
-		if ( !strcmp( s->data, "#" ) ) {
-			if ( i==0 || i==tokens->n-1 ) {
-				fprintf( stderr, "%s: Warning: Stray string concatenation "
-					"('#' character) in reference\n", progname );
-				status = list_remove( tokens, i );
-				if ( status!=LIST_OK ) return BIBL_ERR_MEMERR;
-				continue;
-			}
-			s = list_get( 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.\n", progname );
-			t = list_get( 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.\n", progname );
-			if ( ( s->data[s->len-1]=='\"' && t->data[0]=='\"') || (s->data[s->len-1]=='}' && t->data[0]=='{') ) {
-				newstr_trimend( s, 1 );
-				newstr_trimbegin( t, 1 );
-				newstr_newstrcat( s, t );
-			} else {
-				newstr_newstrcat( s, t );
-			}
-			status = list_remove( tokens, i );
-			if ( status!=LIST_OK ) return BIBL_ERR_MEMERR;
-			status = list_remove( tokens, i );
-			if ( status!=LIST_OK ) return BIBL_ERR_MEMERR;
-		} else i++;
-	}
-	return BIBL_OK;
-}
-
-static char *
-process_biblatexline( char *p, newstr *tag, newstr *data, uchar stripquotes )
-{
-	int i, status;
-	list tokens;
-	newstr *s;
-
-	newstr_empty( data );
-
-	p = biblatex_tag( p, tag );
-	if ( tag->len==0 ) return p;
-
-	list_init( &tokens );
-
-	if ( *p=='=' ) p = biblatex_data( p+1, NULL, &tokens );
-
-	replace_strings( &tokens, NULL );
-
-	status = string_concatenate( &tokens, NULL );
-	if ( status!=BIBL_OK ) {
-		p = NULL;
-		goto out;
-	}
-
-	for ( i=0; i<tokens.n; i++ ) {
-		s = list_get( &tokens, i );
-		if ( ( stripquotes && s->data[0]=='\"' && s->data[s->len-1]=='\"' ) ||
-		     ( s->data[0]=='{' && s->data[s->len-1]=='}' ) ) {
-			newstr_trimbegin( s, 1 );
-			newstr_trimend( s, 1 );
-		}
-		newstr_newstrcat( data, list_get( &tokens, i ) );
-	}
-out:
-	list_free( &tokens );
-	return p;
-}
-
-static int
-process_cite( fields *bibin, char *p, char *filename, long nref )
-{
-	int fstatus, status = BIBL_OK;
-	newstr tag, data;
-	newstrs_init( &tag, &data, NULL );
-	p = process_biblatextype( p, &data );
-	if ( data.len ) {
-		fstatus = fields_add( bibin, "INTERNAL_TYPE", data.data, 0 );
-		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-	}
-	p = process_biblatexid ( p, &data );
-	if ( data.len ) {
-		fstatus = fields_add( bibin, "REFNUM", data.data, 0 );
-		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-	}
-	while ( *p ) {
-		p = process_biblatexline( p, &tag, &data, 1 );
-		if ( !p ) { status = BIBL_ERR_MEMERR; goto out; }
-		/* no anonymous or empty fields allowed */
-		if ( tag.len && data.len ) {
-			fstatus = fields_add( bibin, tag.data, data.data, 0 );
-			if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-		}
-		newstrs_empty( &tag, &data, NULL );
-	}
-out:
-	newstrs_free( &tag, &data, NULL );
-	return status;
-}
-
-/* process_string()
- *
- * Handle lines like:
- *
- * '@STRING{TL = {Tetrahedron Lett.}}'
- *
- * p should point to just after '@STRING'
- *
- * In BibTeX, if a string is defined several times, the last one is kept.
- *
- */
-static int
-process_string( char *p )
-{
-	int n, status = BIBL_OK;
-	newstr s1, s2, *s;
-	newstrs_init( &s1, &s2, NULL );
-	while ( *p && *p!='{' && *p!='(' ) p++;
-	if ( *p=='{' || *p=='(' ) p++;
-	p = process_biblatexline( skip_ws( p ), &s1, &s2, 0 );
-	if ( s2.data ) {
-		newstr_findreplace( &s2, "\\ ", " " );
-		if ( newstr_memerr( &s2 ) ) { status = BIBL_ERR_MEMERR; goto out; }
-	}
-	if ( s1.data ) {
-		n = list_find( &find, s1.data );
-		if ( n==-1 ) {
-			s = list_add( &find, &s1 );
-			if ( s==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
-			if ( s2.data ) s = list_add( &replace, &s2 );
-			else s = list_addc( &replace, "" );
-			if ( s==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
-		} else {
-			if ( s2.data ) s = list_set( &replace, n, &s2 );
-			else s = list_setc( &replace, n, "" );
-			if ( s==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
-		}
-	}
-out:
-	newstrs_free( &s1, &s2, NULL );
-	return status;
-}
-
-int
-biblatexin_processf( fields *bibin, char *data, char *filename, long nref )
-{
-	if ( !strncasecmp( data, "@STRING", 7 ) ) {
-		process_string( data+7 );
-		return 0;
-        } else {
-		process_cite( bibin, data, filename, nref );
-		return 1;
-	}
-}
-
-/*****************************************************
- PUBLIC: void biblatexin_cleanf()
-*****************************************************/
-
-static void
-biblatex_process_tilde( newstr *s )
-{
-	char *p, *q;
-	int n = 0;
-
-	p = q = s->data;
-	if ( !p ) return;
-	while ( *p ) {
-		if ( *p=='~' ) {
-			*q = ' ';
-		} else if ( *p=='\\' && *(p+1)=='~' ) {
-			n++;
-			p++;
-			*q = '~';
-		} else {
-			*q = *p;
-		}
-		p++;
-		q++;
-	}
-	*q = '\0';
-	s->len -= n;
-}
-
-static void
-biblatex_process_bracket( newstr *s )
-{
-	char *p, *q;
-	int n = 0;
-
-	p = q = s->data;
-	if ( !p ) return;
-	while ( *p ) {
-		if ( *p=='\\' && ( *(p+1)=='{' || *(p+1)=='}' ) ) {
-			n++;
-			p++;
-			*q = *p;
-			q++;
-		} else if ( *p=='{' || *p=='}' ) {
-			n++;
-		} else {
-			*q = *p;
-			q++;
-		}
-		p++;
-	}
-	*q = '\0';
-	s->len -= n;
-}
-
-static int
-biblatex_cleantoken( newstr *s )
-{
-	/* 'textcomp' annotations */
-	newstr_findreplace( s, "\\textit", "" );
-	newstr_findreplace( s, "\\textbf", "" );
-	newstr_findreplace( s, "\\textsl", "" );
-	newstr_findreplace( s, "\\textsc", "" );
-	newstr_findreplace( s, "\\textsf", "" );
-	newstr_findreplace( s, "\\texttt", "" );
-	newstr_findreplace( s, "\\textsubscript", "" );
-	newstr_findreplace( s, "\\textsuperscript", "" );
-	newstr_findreplace( s, "\\emph", "" );
-	newstr_findreplace( s, "\\url", "" );
-
-	/* Other text annotations */
-	newstr_findreplace( s, "\\it ", "" );
-	newstr_findreplace( s, "\\em ", "" );
-
-	newstr_findreplace( s, "\\%", "%" );
-	newstr_findreplace( s, "\\$", "$" );
-	while ( newstr_findreplace( s, "  ", " " ) ) {}
-
-	/* 'textcomp' annotations that we don't want to substitute on output*/
-	newstr_findreplace( s, "\\textdollar", "$" );
-	newstr_findreplace( s, "\\textunderscore", "_" );
-
-	biblatex_process_bracket( s );
-	biblatex_process_tilde( s );
-
-	if ( !newstr_memerr( s ) ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
-
-static int
-biblatex_split( list *tokens, newstr *s )
-{
-	int i, n = s->len, nbrackets = 0, status = BIBL_OK;
-	newstr tok, *t;
-
-	newstr_init( &tok );
-
-	for ( i=0; i<n; ++i ) {
-		if ( s->data[i]=='{' && ( i==0 || s->data[i-1]!='\\' ) ) {
-			nbrackets++;
-			newstr_addchar( &tok, '{' );
-		} else if ( s->data[i]=='}' && ( i==0 || s->data[i-1]!='\\' ) ) {
-			nbrackets--;
-			newstr_addchar( &tok, '}' );
-		} else if ( !is_ws( s->data[i] ) || nbrackets ) {
-			newstr_addchar( &tok, s->data[i] );
-		} else if ( is_ws( s->data[i] ) ) {
-			if ( newstr_memerr( &tok ) ) { status = BIBL_ERR_MEMERR; goto out; }
-			if ( tok.len ) {
-				t = list_add( tokens, &tok );
-				if ( !t ) { status = BIBL_ERR_MEMERR; goto out; }
-			}
-			newstr_empty( &tok );
-		}
-	}
-	if ( tok.len ) {
-		if ( newstr_memerr( &tok ) ) { status = BIBL_ERR_MEMERR; goto out; }
-		t = list_add( tokens, &tok );
-		if ( !t ) { status = BIBL_ERR_MEMERR; goto out; }
-	}
-
-	for ( i=0; i<tokens->n; ++i ) {
-		t = list_get( tokens, i );
-		newstr_trimstartingws( t );
-		newstr_trimendingws( t );
-		if ( newstr_memerr( t ) ) { status = BIBL_ERR_MEMERR; goto out; }
-	}
-out:
-	newstr_free( &tok );
-	return status;
-}
-
-static int
-biblatexin_addtitleurl( fields *info, newstr *in )
-{
-	int fstatus, status = BIBL_OK;
-	newstr s;
-	char *p;
-
-	newstr_init( &s );
-	/* skip past "\href{" */
-	p = newstr_cpytodelim( &s, in->data + 6, "}", 1 );
-	if ( newstr_memerr( &s ) ) {
-		status = BIBL_ERR_MEMERR;
-		goto out;
-	}
-	fstatus = fields_add( info, "URL", s.data, 0 );
-	if ( fstatus!=FIELDS_OK ) {
-		status = BIBL_ERR_MEMERR;
-		goto out;
-	}
-
-	p = newstr_cpytodelim( &s, p, "", 0 );
-	if ( newstr_memerr( &s ) ) {
-		status = BIBL_ERR_MEMERR;
-		goto out;
-	}
-	newstr_swapstrings( &s, in );
-out:
-	newstr_free( &s );
-	return status;
-}
-
-static int
-is_name_tag( newstr *tag )
-{
-	if ( tag->len ) {
-		if ( !strcasecmp( tag->data, "author" ) ) return 1;
-		if ( !strcasecmp( tag->data, "editor" ) ) return 1;
-		if ( !strcasecmp( tag->data, "editorb" ) ) return 1;
-		if ( !strcasecmp( tag->data, "editorc" ) ) return 1;
-		if ( !strcasecmp( tag->data, "director" ) ) return 1;
-		if ( !strcasecmp( tag->data, "producer" ) ) return 1;
-		if ( !strcasecmp( tag->data, "execproducer" ) ) return 1;
-		if ( !strcasecmp( tag->data, "writer" ) ) return 1;
-		if ( !strcasecmp( tag->data, "redactor" ) ) return 1;
-		if ( !strcasecmp( tag->data, "annotator" ) ) return 1;
-		if ( !strcasecmp( tag->data, "commentator" ) ) return 1;
-		if ( !strcasecmp( tag->data, "translator" ) ) return 1;
-		if ( !strcasecmp( tag->data, "foreword" ) ) return 1;
-		if ( !strcasecmp( tag->data, "afterword" ) ) return 1;
-		if ( !strcasecmp( tag->data, "introduction" ) ) return 1;
-	}
-	return 0;
-}
-
-static int
-is_url_tag( newstr *tag )
-{
-	if ( tag->len ) {
-		if ( !strcasecmp( tag->data, "url" ) ) return 1;
-	}
-	return 0;
-}
-
-static int
-biblatexin_cleandata( newstr *tag, newstr *s, fields *info, param *p )
-{
-	list tokens;
-	newstr *tok;
-	int i, status = BIBL_OK;
-	if ( !s->len ) return status;
-	/* protect url from undergoing any parsing */
-	if ( is_url_tag( tag ) ) return status;
-	list_init( &tokens );
-	biblatex_split( &tokens, s );
-	for ( i=0; i<tokens.n; ++i ) {
-		if (!strncasecmp(tokens.str[i].data,"\\href{", 6)) {
-			status = biblatexin_addtitleurl( info, &(tokens.str[i]) );
-			if ( status!=BIBL_OK ) goto out;
-		}
-		if ( p && p->latexin && !is_name_tag( tag ) ) {
-			status = biblatex_cleantoken( &(tokens.str[i]) );
-			if ( status!=BIBL_OK ) goto out;
-		}
-	}
-	newstr_empty( s );
-	for ( i=0; i<tokens.n; ++i ) {
-		tok = list_get( &tokens, i );
-		if ( i>0 ) newstr_addchar( s, ' ' );
-		newstr_newstrcat( s, tok );
-	}
-out:
-	list_free( &tokens );
-	return status;
-}
-
-static long
-biblatexin_findref( bibl *bin, char *citekey )
-{
-	int n;
-	long i;
-	for ( i=0; i<bin->nrefs; ++i ) {
-		n = fields_find( bin->ref[i], "refnum", -1 );
-		if ( n==-1 ) continue;
-		if ( !strcmp( bin->ref[i]->data[n].data, citekey ) ) return i;
-	}
-	return -1;
-}
-
-static void
-biblatexin_nocrossref( bibl *bin, long i, int n, param *p )
-{
-	int n1 = fields_find( bin->ref[i], "REFNUM", -1 );
-	if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-	fprintf( stderr, "Cannot find cross-reference '%s'", 
-			bin->ref[i]->data[n].data);
-	if ( n1!=-1 )
-		fprintf( stderr, " for reference '%s'\n", 
-				bin->ref[i]->data[n1].data );
-	fprintf( stderr, "\n" );
-}
-
-static int
-biblatexin_crossref_oneref( fields *ref, fields *cross )
-{
-	int j, nl, ntype, fstatus;
-	char *type, *nt, *nd;
-	ntype = fields_find( ref, "INTERNAL_TYPE", -1 );
-	type = ( char * ) fields_value( ref, ntype, FIELDS_CHRP_NOUSE );
-	for ( j=0; j<cross->n; ++j ) {
-		nt = ( char * ) fields_tag( cross, j, FIELDS_CHRP_NOUSE );
-		if ( !strcasecmp( nt, "INTERNAL_TYPE" ) ) continue;
-		if ( !strcasecmp( nt, "REFNUM" ) ) continue;
-		if ( !strcasecmp( nt, "TITLE" ) ) {
-			if ( !strcasecmp( type, "Inproceedings" ) ||
-			     !strcasecmp( type, "Incollection" ) )
-				nt = "booktitle";
-		}
-		nd = ( char * ) fields_value( cross, j, FIELDS_CHRP_NOUSE );
-		nl = fields_level( cross, j ) + 1;
-		fstatus = fields_add( ref, nt, nd, nl );
-		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-	}
-	return BIBL_OK;
-}
-
-static int
-biblatexin_crossref( bibl *bin, param *p )
-{
-	int n, ncross, status = BIBL_OK;
-	fields *ref, *cross;
-	long i;
-        for ( i=0; i<bin->nrefs; ++i ) {
-		ref = bin->ref[i];
-		n = fields_find( ref, "CROSSREF", -1 );
-		if ( n==-1 ) continue;
-		fields_setused( ref, n );
-		ncross = biblatexin_findref(bin, (char*)fields_value(ref,n, FIELDS_CHRP_NOUSE));
-		if ( ncross==-1 ) {
-			biblatexin_nocrossref( bin, i, n, p );
-			continue;
-		}
-		cross = bin->ref[ncross];
-		status = biblatexin_crossref_oneref( ref, cross );
-		if ( status!=BIBL_OK ) return status;
-	}
-	return status;
-}
-
-static int
-biblatexin_cleanref( fields *bibin, param *p )
-{
-	int i, n, status;
-	newstr *t, *d;
-	n = fields_num( bibin );
-	for ( i=0; i<n; ++i ) {
-		t = fields_tag( bibin, i, FIELDS_STRP_NOUSE );
-		d = fields_value( bibin, i, FIELDS_STRP_NOUSE );
-		status = biblatexin_cleandata( t, d, bibin, p );
-		if ( status!=BIBL_OK ) return status;
-		if ( !strsearch( t->data, "AUTHORS" ) ) {
-			newstr_findreplace( d, "\n", " " );
-			newstr_findreplace( d, "\r", " " );
-		}
-		else if ( !strsearch( t->data, "ABSTRACT" ) ||
-		     !strsearch( t->data, "SUMMARY" ) || 
-		     !strsearch( t->data, "NOTE" ) ) {
-			newstr_findreplace( d, "\n", "" );
-			newstr_findreplace( d, "\r", "" );
-		}
-	}
-	return BIBL_OK;
-}
-
-int
-biblatexin_cleanf( bibl *bin, param *p )
-{
-	int status;
-	long i;
-        for ( i=0; i<bin->nrefs; ++i ) {
-		status = biblatexin_cleanref( bin->ref[i], p );
-		if ( status!=BIBL_OK ) return status;
-	}
-	status = biblatexin_crossref( bin, p );
-	return status;
-}
-
-/*****************************************************
- PUBLIC: void biblatexin_typef()
-*****************************************************/
-
-int
-biblatexin_typef( fields *bibin, char *filename, int nrefs, param *p,
-                variants *all, int nall )
-{
-        char *refnum = "";
-        int reftype, n, nrefnum;
-        n = fields_find( bibin, "INTERNAL_TYPE", 0 );
-        nrefnum = fields_find( bibin, "REFNUM", 0 );
-        if ( nrefnum!=-1 ) refnum = (bibin->data[nrefnum]).data;
-        if ( n!=-1 )
-                /* figure out type */
-                reftype = get_reftype( (bibin->data[n]).data, nrefs,
-                        p->progname, all, nall, refnum );
-        else
-                /* no type info, go for default */
-                reftype = get_reftype( "", nrefs, p->progname, all, nall, refnum );
-        return reftype;
-}
-
-/*****************************************************
- PUBLIC: int biblatexin_convertf(), returns BIBL_OK or BIBL_ERR_MEMERR
-*****************************************************/
-
-/* is_utf8_emdash()
- *
- * Internally pages="A---B" will convert --- to a UTF8
- * emdash = 0xE2 (-30) 0x80 (-128) 0x94 (-108)
- */
-static int
-is_utf8_emdash( char *p )
-{
-	static char emdash[3] = { -30, -128, -108 };
-	if ( strncmp( p, emdash, 3 ) ) return 0;
-	return 1;
-}
-/* is_utf8_endash()
- *
- * Internally pages="A--B" will convert -- to a UTF8
- * endash = 0xE2 (-30) 0x80 (-128) 0x93 (-109)
- */
-static int
-is_utf8_endash( char *p )
-{
-	static char endash[3] = { -30, -128, -109 };
-	if ( strncmp( p, endash, 3 ) ) return 0;
-	return 1;
-}
-
-static int
-process_pages( fields *info, newstr *s, int level )
-{
-	int fstatus, status = BIBL_OK;
-	newstr page;
-	char *p;
-
-	newstr_findreplace( s, " ", "" );
-	if ( s->len==0 ) return status;
-
-	newstr_init( &page );
-	p = skip_ws( s->data );
-	while ( *p && !is_ws(*p) && *p!='-' && *p!=-30 )
-		newstr_addchar( &page, *p++ );
-	if ( page.len>0 ) {
-		fstatus = fields_add( info, "PAGESTART", page.data, level );
-		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-	}
-
-	while ( *p && (is_ws(*p) || *p=='-' ) ) p++;
-	if ( *p && is_utf8_emdash( p ) ) p+=3;
-	if ( *p && is_utf8_endash( p ) ) p+=3;
-
-	newstr_empty( &page );
-	while ( *p && !is_ws(*p) && *p!='-' && *p!=-30 )
-		newstr_addchar( &page, *p++ );
-	if ( page.len>0 ) {
-		fstatus = fields_add( info, "PAGEEND", page.data, level );
-		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-	}
-out:
-	newstr_free( &page );
-	return status;
-}
-
-static int
-process_url( fields *info, char *p, int level )
-{
-	int fstatus;
-	if ( !strncasecmp( p, "\\urllink", 8 ) )
-		fstatus = fields_add( info, "URL", p+8, level );
-	else if ( !strncasecmp( p, "\\url", 4 ) )
-		fstatus = fields_add( info, "URL", p+4, level );
-	else if ( !strncasecmp( p, "arXiv:", 6 ) )
-		fstatus = fields_add( info, "ARXIV", p+6, level ); 
-	else if ( !strncasecmp( p, "http://arxiv.org/abs/", 21 ) )
-		fstatus = fields_add( info, "ARXIV", p+21, level );
-	else if ( !strncasecmp( p, "http:", 5 ) )
-		fstatus = fields_add( info, "URL", p, level );
-	else 
-		fstatus = fields_add( info, "URL", p, level );
-	if ( fstatus==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
-
-
-/* process_howpublished()
- *
- *    howpublished={},
- *
- * Normally indicates the manner in which something was
- * published in lieu of a formal publisher, so typically
- * 'howpublished' and 'publisher' will never be in the
- * same reference.
- *
- * Occasionally, people put Diploma thesis information
- * into this field, so check for that first.
- */
-static int
-process_howpublished( fields *info, char *p, int level )
-{
-	int fstatus;
-        if ( !strncasecmp( p, "Diplom", 6 ) )
-                fstatus = fields_replace_or_add( info, "NGENRE", "Diploma thesis", level );
-        else if ( !strncasecmp( p, "Habilitation", 13 ) )
-                fstatus = fields_replace_or_add( info, "NGENRE", "Habilitation thesis", level );
-        else
-		fstatus = fields_add( info, "PUBLISHER", p, level );
-	if ( fstatus==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
-
-static int
-process_thesistype( fields *info, char *p, int level )
-{
-	int fstatus;
-	/* type in the @thesis is used to distinguish Ph.D. and Master's thesis */
-	if ( !strncasecmp( p, "phdthesis", 9 ) ) {
-		fstatus = fields_replace_or_add( info, "NGENRE", "Ph.D. thesis", level );
-	} else if ( !strncasecmp( p, "mastersthesis", 13 ) || !strncasecmp( p, "masterthesis", 12 ) ) {
-		fstatus = fields_replace_or_add( info, "NGENRE", "Masters thesis", level );
-	} else if ( !strncasecmp( p, "mathesis", 8 ) ) {
-		fstatus = fields_replace_or_add( info, "NGENRE", "Masters thesis", level );
-	} else if ( !strncasecmp( p, "diploma", 7 ) ) {
-		fstatus = fields_replace_or_add( info, "NGENRE", "Diploma thesis", level );
-	} else if ( !strncasecmp( p, "habilitation", 12 ) ) {
-		fstatus = fields_replace_or_add( info, "NGENRE", "Habilitation thesis", level );
-	}
-	if ( fstatus==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
-
-/* biblatex drops school field if institution is present */
-static int
-process_school( fields *bibin, fields *info, char *tag, char *value, int level )
-{
-	int fstatus;
-	if ( fields_find( bibin, "institution", LEVEL_ANY ) != -1 )
-		return BIBL_OK;
-	else {
-		fstatus = fields_add( info, tag, value, level );
-		if ( fstatus==FIELDS_OK ) return BIBL_OK;
-		else return BIBL_ERR_MEMERR;
-	}
-}
-
-/* biblatex drops school field if institution is present */
-static int
-process_subtype( fields *bibin, fields *info, char *tag, char *value, int level )
-{
-	int fstatus;
-	if ( !strcasecmp( value, "magazine" ) ) {
-		fstatus = fields_add( info, "NGENRE", "magazine article", LEVEL_MAIN );
-		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-		fstatus = fields_add( info, "NGENRE", "magazine", LEVEL_HOST );
-		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-	} else if ( !strcasecmp( value, "newspaper" ) ) {
-		fstatus = fields_add( info, "NGENRE", "newspaper article", LEVEL_MAIN );
-		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-		fstatus = fields_add( info, "GENRE", "newspaper", LEVEL_HOST );
-		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-	}
-	return BIBL_OK;
-}
-
-static int
-process_eprint( fields *bibin, fields *info, int level )
-{
-	int neprint, netype, fstatus;
-	char *eprint = NULL, *etype = NULL;
-	neprint = fields_find( bibin, "eprint", -1 );
-	netype  = fields_find( bibin, "eprinttype", -1 );
-	if ( neprint!=-1 ) eprint = bibin->data[neprint].data;
-	if ( netype!=-1 ) etype = bibin->data[netype].data;
-	if ( eprint && etype ) {
-		if ( !strncasecmp( etype, "arxiv", 5 ) ) {
-			fstatus = fields_add( info, "ARXIV", eprint, level );
-			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-		} else if ( !strncasecmp( etype, "jstor", 5 ) ) {
-			fstatus = fields_add( info, "JSTOR", eprint, level );
-			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-		} else if ( !strncasecmp( etype, "pubmed", 6 ) ) {
-			fstatus = fields_add( info, "PMID", eprint, level );
-			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-		} else if ( !strncasecmp( etype, "medline", 7 ) ) {
-			fstatus = fields_add( info, "MEDLINE", eprint, level );
-			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-		} else {
-			fstatus = fields_add( info, "EPRINT", eprint, level );
-			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-			fstatus = fields_add( info, "EPRINTTYPE", etype, level );
-			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-		}
-		fields_setused( bibin, neprint );
-		fields_setused( bibin, netype );
-	} else if ( eprint ) {
-		fstatus = fields_add( info, "EPRINT", eprint, level );
-		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-		fields_setused( bibin, neprint );
-	} else if ( etype ) {
-		fstatus = fields_add( info, "EPRINTTYPE", etype, level );
-		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-		fields_setused( bibin, netype );
-	}
-	return BIBL_OK;
-}
-
-static void
-report( fields *f )
-{
-	int i, n;
-	n = fields_num( f );
-	for ( i=0; i<n; ++i )
-		fprintf(stderr, "%d '%s' = '%s'\n",
-			fields_level( f, i ),
-			(char*)fields_tag( f, i, FIELDS_CHRP_NOUSE ),
-			(char*)fields_value( f, i, FIELDS_CHRP_NOUSE ) );
-}
-
-static void
-biblatexin_notag( param *p, char *tag )
-{
-	if ( p->verbose && strcmp( tag, "INTERNAL_TYPE" ) ) {
-		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-		fprintf( stderr, " Cannot find tag '%s'\n", tag );
-	}
-}
-
-/* get_title_elements()
- *
- * find all of the biblatex title elements for the current level
- *    internal "TITLE"      -> "title", "booktitle", "maintitle"
- *    internal "SUBTITLE"   -> "subtitle", "booksubtitle", "mainsubtitle"
- *    internal "TITLEADDON" -> "titleaddon", "booktitleaddon", "maintitleaddon"
- *
- * place in ttl, subttl, and ttladdon strings
- *
- * return 1 if an element is found, 0 if not
- */
-static int
-get_title_elements( fields *bibin, int currlevel, int reftype, variants *all, int nall,
-	newstr *ttl, newstr *subttl, newstr *ttladdon )
-{
-	int nfields, process, level, i, n;
-	newstr *t, *d;
-	char *newtag;
-
-	newstrs_empty( ttl, subttl, ttladdon, NULL );
-
-	nfields = fields_num( bibin );
-
-	for ( i=0; i<nfields; ++i ) {
-
-		/* ...skip already used titles */
-		if ( fields_used( bibin, i ) ) continue;
-
-		/* ...skip empty elements */
-		t = fields_tag  ( bibin, i, FIELDS_STRP_NOUSE );
-		d = fields_value( bibin, i, FIELDS_STRP_NOUSE );
-		if ( d->len == 0 ) continue;
-
-		n = translate_oldtag( t->data, reftype, all, nall, &process, &level, &newtag );
-		if ( n==-1 ) continue;
-		if ( process != TITLE ) continue;
-		if ( level != currlevel ) continue;
-
-		fields_setused( bibin, i );
-
-		if ( !strcasecmp( newtag, "TITLE" ) ) {
-			if ( ttl->len ) newstr_addchar( ttl, ' ' );
-			newstr_newstrcat( ttl, d );
-		} else if ( !strcasecmp( newtag, "SUBTITLE" ) ) {
-			if ( subttl->len ) newstr_addchar( subttl, ' ' );
-			newstr_newstrcat( subttl, d );
-		} else if ( !strcasecmp( newtag, "TITLEADDON" ) ) {
-			if ( ttladdon->len ) newstr_addchar( ttladdon, ' ' );
-			newstr_newstrcat( ttladdon, d );
-		}
-	}
-
-	return ( ttl->len>0 || subttl->len > 0 || ttladdon->len > 0 );
-}
-
-/* attach_addon()
- *
- * Add titleaddon to the title.
- */
-static void
-attach_addon( newstr *title, newstr *addon )
-{
-	if ( title->len ) {
-		if ( title->data[title->len-1]!='.' )
-			newstr_addchar( title, '.' );
-		newstr_addchar( title, ' ' );
-	}
-	newstr_newstrcat( title, addon );
-}
-
-static int
-process_combined_title( fields *info, newstr *ttl, newstr *subttl, newstr *ttladdon, int currlevel )
-{
-	int fstatus, status = BIBL_OK;
-	newstr combined;
-
-	newstr_init( &combined );
-	newstr_newstrcpy( &combined, ttl );
-	if ( subttl->len ) {
-		if ( combined.len && combined.data[combined.len-1]!=':' && combined.data[combined.len-1]!='?' )
-			newstr_addchar( &combined, ':' );
-		newstr_addchar( &combined, ' ' );
-		newstr_newstrcat( &combined, subttl );
-	}
-	if ( ttladdon->len ) attach_addon( &combined, ttladdon );
-	if ( newstr_memerr( &combined ) ) { status = BIBL_ERR_MEMERR; goto out; }
-	fstatus = fields_add( info, "TITLE", combined.data, currlevel );
-	if ( fstatus==FIELDS_OK ) status = BIBL_ERR_MEMERR;
-out:
-	newstr_free( &combined );
-	return status;
-}
-
-static int
-process_separated_title( fields *info, newstr *ttl, newstr *subttl, newstr *ttladdon, int currlevel )
-{
-	int fstatus;
-	if ( ttladdon->len ) {
-		if ( subttl->len ) attach_addon( subttl, ttladdon );
-		else attach_addon( ttl, ttladdon );
-	}
-	if ( ttl->len ) {
-		fstatus = fields_add( info, "TITLE", ttl->data, currlevel );
-		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-	}
-	if ( subttl->len ) {
-		fstatus = fields_add( info, "SUBTITLE", subttl->data, currlevel );
-		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-	}
-	return BIBL_OK;
-}
-
-static int
-process_title_all( fields *bibin, fields *info, int reftype, param *p,
-	variants *all, int nall )
-{
-	int currlevel, found, status = BIBL_OK;
-	newstr ttl, subttl, ttladdon;
-	newstrs_init( &ttl, &subttl, &ttladdon, NULL );
-	for ( currlevel = 0; currlevel<LEVEL_SERIES+2; currlevel++ ) {
-		found = get_title_elements( bibin, currlevel, reftype, all, nall,
-				&ttl, &subttl, &ttladdon );
-		if ( !found ) continue;
-		if ( p->nosplittitle )
-			status = process_combined_title( info, &ttl, &subttl, &ttladdon, currlevel );
-		else
-			status = process_separated_title( info, &ttl, &subttl, &ttladdon, currlevel );
-		if ( status!=BIBL_OK ) goto out;
-	}
-out:
-	newstrs_free( &ttl, &subttl, &ttladdon, NULL );
-	return status;
-}
-
-
-static int
-biblatex_matches_list( fields *info, char *tag, char *suffix, newstr *data, int level,
-                list *names, int *match )
-{
-	int i, fstatus, status = BIBL_OK;
-	newstr newtag;
-
-	*match = 0;
-	if ( names->n==0 ) return status;
-
-	newstr_init( &newtag );
-
-	for ( i=0; i<names->n; ++i ) {
-		if ( strcmp( data->data, list_getc( names, i ) ) ) continue;
-		newstr_initstr( &newtag, tag );
-		newstr_strcat( &newtag, suffix );
-		fstatus = fields_add( info, newtag.data, data->data, level );
-		if ( fstatus!=FIELDS_OK ) {
-			status = BIBL_ERR_MEMERR;
-			goto out;
-		}
-		*match = 1;
-		goto out;
-	}
-
-out:
-	newstr_free( &newtag );
-	return status;
-}
-
-static int
-biblatex_names( fields *info, char *tag, newstr *data, int level, list *asis, list *corps )
-{
-	int begin, end, ok, n, etal, i, match, status = BIBL_OK;
-	list tokens;
-
-	/* If we match the asis or corps list add and bail. */
-	status = biblatex_matches_list( info, tag, ":ASIS", data, level, asis, &match );
-	if ( match==1 || status!=BIBL_OK ) return status;
-	status = biblatex_matches_list( info, tag, ":CORP", data, level, corps, &match );
-	if ( match==1 || status!=BIBL_OK ) return status;
-
-	list_init( &tokens );
-
-	biblatex_split( &tokens, data );
-	for ( i=0; i<tokens.n; ++i )
-		biblatex_cleantoken( list_get( &tokens, i ) );
-
-	etal = name_findetal( &tokens );
-
-	begin = 0;
-	n = tokens.n - etal;
-	while ( begin < n ) {
-
-		end = begin + 1;
-
-		while ( end < n && strcasecmp( list_getc( &tokens, end ), "and" ) )
-			end++;
-
-		if ( end - begin == 1 ) {
-			ok = name_addsingleelement( info, tag, list_getc( &tokens, begin ), level, 0 );
-			if ( !ok ) { status = BIBL_ERR_MEMERR; goto out; }
-		} else {
-			ok = name_addmultielement( info, tag, &tokens, begin, end, level );
-			if ( !ok ) { status = BIBL_ERR_MEMERR; goto out; }
-		}
-
-		begin = end + 1;
-
-		/* Handle repeated 'and' errors */
-		while ( begin < n && !strcasecmp( list_getc( &tokens, begin ), "and" ) )
-			begin++;
-
-	}
-
-	if ( etal ) {
-		ok = name_addsingleelement( info, tag, "et al.", level, 0 );
-		if ( !ok ) status = BIBL_ERR_MEMERR;
-	}
-
-out:
-	list_free( &tokens );
-	return status;
-}
-
-/*
- * biblatex has multiple editor fields "editor", "editora", "editorb", "editorc",
- * each of which can be modified from a type of "EDITOR" via "editortype",
- * "editoratype", "editorbtype", "editorctype".
- *
- * Defined types:
- *     "editor"
- *     "collaborator"
- *     "compiler"
- *     "redactor"
- *
- *     "reviser" ?
- *     "founder" ?
- *     "continuator" ?
- *
- *  bibtex-chicago
- *
- *     "director"
- *     "producer"
- *     "conductor"
- *     "none" (for performer)
- */
-static int
-process_editor( fields *bibin, fields *info, newstr *tag, newstr *value, int level, list *asis, list *corps )
-{
-	char *editor_fields[] = { "editor", "editora", "editorb", "editorc" };
-	char *editor_types[]  = { "editortype", "editoratype", "editorbtype", "editorctype" };
-	int i, n = 0, ntype, neditors = sizeof( editor_fields ) / sizeof( editor_fields[0] );
-	char *type, *outtag = "EDITOR";
-	for ( i=1; i<neditors; ++i )
-		if ( !strcasecmp( tag->data, editor_fields[i] ) ) n = i;
-	ntype = fields_find( bibin, editor_types[n], LEVEL_ANY );
-	if ( ntype!=-1 ) {
-		type = fields_value( bibin, ntype, FIELDS_CHRP_NOUSE );
-		if ( !strcasecmp( type, "collaborator" ) )  outtag = "COLLABORATOR";
-		else if ( !strcasecmp( type, "compiler" ) ) outtag = "COMPILER";
-		else if ( !strcasecmp( type, "redactor" ) ) outtag = "REDACTOR";
-		else if ( !strcasecmp( type, "director" ) ) outtag = "DIRECTOR";
-		else if ( !strcasecmp( type, "producer" ) ) outtag = "PRODUCER";
-		else if ( !strcasecmp( type, "none" ) )     outtag = "PERFORMER";
-	}
-	return biblatex_names( info, outtag, value, level, asis, corps );
-}
-
-static int
-biblatexin_simple( fields *f, char *tag, char *value, int level )
-{
-	int fstatus = fields_add( f, tag, value, level );
-	if ( fstatus==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
-
-int
-biblatexin_convertf( fields *bibin, fields *info, int reftype, param *p,
-		variants *all, int nall )
-{
-	int process, level, i, n, nfields, status = BIBL_OK;
-	newstr *t, *d;
-	char *newtag;
-
-	nfields = fields_num( bibin );
-	for ( i=0; i<nfields; ++i ) {
-
-               /* skip ones already "used" such as successful crossref */
-                if ( fields_used( bibin, i ) ) continue;
-
-		/* skip ones with no data or no tags (e.g. don't match ALWAYS/DEFAULT entries) */
-		t = fields_tag  ( bibin, i, FIELDS_STRP_NOUSE );
-		d = fields_value( bibin, i, FIELDS_STRP_NOUSE );
-		if ( t->len == 0 || d->len == 0 ) continue;
-
-		n = translate_oldtag( t->data, reftype, all, nall, &process, &level, &newtag );
-		if ( n==-1 ) {
-			biblatexin_notag( p, t->data );
-			continue;
-		}
-
-		switch ( process ) {
-
-		case SIMPLE:
-			status = biblatexin_simple( info, newtag, d->data, level );
-			fields_setused( bibin, i );
-			break;
-
-		case PERSON:
-			status = biblatex_names( info, newtag, d, level, &(p->asis), &(p->corps) );
-			fields_setused( bibin, i );
-			break;
-
-		case BLT_EDITOR:
-			status = process_editor( bibin, info, t, d, level, &(p->asis), &(p->corps) );
-			fields_setused( bibin, i );
-			break;
-
-		case PAGES:
-			status = process_pages( info, d, level);
-			fields_setused( bibin, i );
-			break;
-
-		case HOWPUBLISHED:
-			status = process_howpublished( info, d->data, level );
-			fields_setused( bibin, i );
-			break;
-
-		case BT_URL:
-			status = process_url( info, d->data, level );
-			fields_setused( bibin, i );
-			break;
-
-		case BT_GENRE:
-			status = biblatexin_simple( info, "NGENRE", d->data, level );
-			fields_setused( bibin, i );
-			break;
-
-		case BT_EPRINT:
-			status = process_eprint( bibin, info, level );
-			fields_setused( bibin, i );
-			break;
-
-		case BLT_THESIS_TYPE:
-			status = process_thesistype( info, d->data, level );
-			fields_setused( bibin, i );
-			break;
-
-		case BLT_SCHOOL:
-			status = process_school( bibin, info, newtag, d->data, level );
-			fields_setused( bibin, i );
-			break;
-
-		case BLT_SUBTYPE:
-			status = process_subtype( bibin, info, newtag, d->data, level );
-			fields_setused( bibin, i );
-			break;
-
-		case BLT_SKIP:
-			status = BIBL_OK;
-			fields_setused( bibin, i );
-			break;
-
-		case TITLE:
-			status = BIBL_OK; /* delay title processing until later */
-			break;
-
-		default:
-			status = BIBL_OK;
-			break;
-
-		}
-		if ( status!=BIBL_OK ) return status;
-
-	}
-
-	status = process_title_all( bibin, info, reftype, p, all, nall );
-
-	if ( status==BIBL_OK && p->verbose ) report( info );
+ * Copyright (c) Chris Putnam 2008-2017
+ * Copyright (c) Johannes Wilm 2010-2017
+ *
+ * Program and source code released under the GPL version 2
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "is_ws.h"
+#include "strsearch.h"
+#include "str.h"
+#include "utf8.h"
+#include "str_conv.h"
+#include "fields.h"
+#include "slist.h"
+#include "name.h"
+#include "reftypes.h"
+#include "bibformats.h"
+#include "generic.h"
+
+extern variants biblatex_all[];
+extern int biblatex_nall;
+
+static slist find    = { 0, 0, 0, NULL };
+static slist replace = { 0, 0, 0, NULL };
+
+/*****************************************************
+ PUBLIC: void biblatexin_initparams()
+*****************************************************/
+
+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_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 );
+
+void
+biblatexin_initparams( param *p, 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;
+
+	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;
+
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
+
+	if ( !progname ) p->progname = NULL;
+	else p->progname = strdup( progname );
+}
+
+/*****************************************************
+ PUBLIC: int biblatexin_readf()
+*****************************************************/
+
+/*
+ * readf can "read too far", so we store this information in line, thus
+ * the next new text is in line, either from having read too far or
+ * from the next chunk obtained via str_fget()
+ *
+ * return 1 on success, 0 on error/end-of-file
+ *
+ */
+static int
+readmore( FILE *fp, char *buf, int bufsize, int *bufpos, str *line )
+{
+	if ( line->len ) return 1;
+	else return str_fget( fp, buf, bufsize, bufpos, line );
+}
+
+/*
+ * readf()
+ *
+ * returns zero if cannot get reference and hit end of-file
+ * returns 1 if last reference in file, 2 if reference within file
+ */
+static int
+biblatexin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
+{
+	int haveref = 0;
+	char *p;
+	while ( haveref!=2 && readmore( fp, buf, bufsize, bufpos, line ) ) {
+		if ( line->len == 0 ) continue; /* blank line */
+		p = &(line->data[0]);
+		p = skip_ws( p );
+		if ( *p == '%' ) { /* commented out line */
+			str_empty( line );
+			continue;
+		}
+		if ( *p == '@' ) haveref++;
+		if ( haveref && haveref<2 ) {
+			str_strcatc( reference, p );
+			str_addchar( reference, '\n' );
+			str_empty( line );
+		} else if ( !haveref ) str_empty( line );
+	
+	}
+	*fcharset = CHARSET_UNKNOWN;
+	return haveref;
+}
+
+/*****************************************************
+ PUBLIC: int biblatexin_processf()
+*****************************************************/
+
+static char *
+process_biblatextype( char *p, str *type )
+{
+	str tmp;
+	str_init( &tmp );
+
+	if ( *p=='@' ) p++;
+	p = str_cpytodelim( &tmp, p, "{( \t\r\n", 0 );
+	p = skip_ws( p );
+	if ( *p=='{' || *p=='(' ) p++;
+	p = skip_ws( p );
+
+	if ( str_has_value( &tmp ) ) str_strcpy( type, &tmp );
+	else str_empty( type );
+
+	str_free( &tmp );
+	return p;
+}
+
+static char *
+process_biblatexid( char *p, str *id )
+{
+	char *start_p = p;
+	str tmp;
+
+	str_init( &tmp );
+	p = str_cpytodelim( &tmp, p, ",", 1 );
+
+	if ( str_has_value( &tmp ) ) {
+		if ( strchr( tmp.data, '=' ) ) {
+			/* Endnote writes biblatex files w/o fields, try to
+			 * distinguish via presence of an equal sign.... if
+			 * it's there, assume that it's a tag/data pair instead
+			 * and roll back.
+			 */
+			p = start_p;
+			str_empty( id );
+		} else {
+			str_strcpy( id, &tmp );
+		}
+	} else {
+		str_empty( id );
+	}
+
+	str_free( &tmp );
+	return skip_ws( p );
+}
+
+static char *
+biblatex_tag( 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 )
+{
+	unsigned int nbracket = 0, nquotes = 0;
+	char *startp = p;
+	str tok, *s;
+
+	str_init( &tok );
+	while ( p && *p ) {
+		if ( !nquotes && !nbracket ) {
+			if ( *p==',' || *p=='=' || *p=='}' || *p==')' )
+				goto out;
+		}
+		if ( *p=='\"' && nbracket==0 && ( p==startp || *(p-1)!='\\' ) ) {
+			nquotes = !nquotes;
+			str_addchar( &tok, *p );
+			if ( !nquotes ) {
+				s = slist_add( tokens, &tok );
+				if ( !s ) { 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; }
+			}
+			str_strcpyc( &tok, "#" );
+			s = slist_add( tokens, &tok );
+			if ( !s ) { p = NULL; goto outerr; }
+			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 ) {
+				s = slist_add( tokens, &tok );
+				if ( !s ) { p = NULL; goto outerr; }
+				str_empty( &tok );
+			}
+		} else if ( !is_ws( *p ) || nquotes || nbracket ) {
+			if ( !is_ws( *p ) ) str_addchar( &tok, *p );
+			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, ' ' );
+					while ( is_ws( *(p+1) ) ) p++;
+				}
+			}
+		} else if ( is_ws( *p ) ) {
+			if ( str_has_value( &tok ) ) {
+				s = slist_add( tokens, &tok );
+				if ( !s ) { p = NULL; goto outerr; }
+				str_empty( &tok );
+			}
+		}
+		p++;
+	}
+out:
+	if ( nbracket!=0 ) {
+		fprintf( stderr, "%s: Mismatch in number of brackets in reference %ld\n", pm->progname, nref );
+	}
+	if ( nquotes!=0 ) {
+		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;
+	}
+outerr:
+	str_free( &tok );
+	return p;
+}
+
+/* replace_strings()
+ *
+ * do string replacement -- only if unprotected by quotation marks or curly brackets
+ */
+static void
+replace_strings( slist *tokens, fields *bibin, long nref, param *pm )
+{
+	int i, n, ok;
+	char *q;
+	str *s;
+	i = 0;
+	while ( i < tokens->n ) {
+		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++;
+				}
+				if ( !ok ) {
+					fprintf( stderr, "%s: Warning: Non-numeric "
+					   "BibTeX elements should be in quotations or "
+					   "curly brackets in reference %ld\n", pm->progname, nref );
+				}
+			}
+		}
+		i++;
+	}
+}
+
+static int
+string_concatenate( slist *tokens, fields *bibin, long nref, param *pm )
+{
+	int i, status;
+	str *s, *t;
+	i = 0;
+	while ( i < tokens->n ) {
+		s = slist_str( tokens, i );
+		if ( !strcmp( str_cstr( s ), "#" ) ) {
+			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;
+			status = slist_remove( tokens, i );
+			if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
+		} else i++;
+	}
+	return BIBL_OK;
+}
+
+static char *
+process_biblatexline( char *p, str *tag, str *data, uchar stripquotes, long nref, param *pm )
+{
+	int i, status;
+	slist tokens;
+	str *s;
+
+	str_empty( data );
+
+	p = biblatex_tag( p, tag );
+	if ( tag->len==0 ) return p;
+
+	slist_init( &tokens );
+
+	if ( *p=='=' ) p = biblatex_data( p+1, NULL, &tokens, nref, pm );
+
+	replace_strings( &tokens, NULL, nref, pm );
+
+	status = string_concatenate( &tokens, NULL, nref, pm );
+	if ( status!=BIBL_OK ) {
+		p = NULL;
+		goto out;
+	}
+
+	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 ) );
+	}
+out:
+	slist_free( &tokens );
+	return p;
+}
+
+static int
+process_cite( fields *bibin, char *p, char *filename, long nref, param *pm )
+{
+	int fstatus, status = BIBL_OK;
+	str tag, data;
+	strs_init( &tag, &data, NULL );
+	p = process_biblatextype( p, &data );
+	if ( str_has_value( &data ) ) {
+		fstatus = fields_add( bibin, "INTERNAL_TYPE", str_cstr( &data ), 0 );
+		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
+	}
+	p = process_biblatexid ( p, &data );
+	if ( str_has_value( &data ) ) {
+		fstatus = fields_add( bibin, "REFNUM", str_cstr( &data ), 0 );
+		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
+	}
+	while ( *p ) {
+		p = process_biblatexline( p, &tag, &data, 1, nref, pm );
+		if ( !p ) { 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 );
+	}
+out:
+	strs_free( &tag, &data, NULL );
+	return status;
+}
+
+/* process_string()
+ *
+ * Handle lines like:
+ *
+ * '@STRING{TL = {Tetrahedron Lett.}}'
+ *
+ * p should point to just after '@STRING'
+ *
+ * In BibTeX, if a string is defined several times, the last one is kept.
+ *
+ */
+static int
+process_string( char *p, long nref, param *pm )
+{
+	int n, status = BIBL_OK;
+	str s1, s2, *s;
+	strs_init( &s1, &s2, NULL );
+	while ( *p && *p!='{' && *p!='(' ) p++;
+	if ( *p=='{' || *p=='(' ) p++;
+	p = process_biblatexline( skip_ws( p ), &s1, &s2, 0, nref, pm );
+	if ( str_has_value( &s2 ) ) {
+		str_findreplace( &s2, "\\ ", " " );
+		if ( str_memerr( &s2 ) ) { status = BIBL_ERR_MEMERR; goto out; }
+	}
+	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; }
+		} else {
+			if ( str_has_value( &s2 ) ) s = slist_set( &replace, n, &s2 );
+			else s = slist_setc( &replace, n, "" );
+			if ( s==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
+		}
+	}
+out:
+	strs_free( &s1, &s2, NULL );
+	return status;
+}
+
+static int
+biblatexin_processf( fields *bibin, char *data, char *filename, long nref, param *p )
+{
+	if ( !strncasecmp( data, "@STRING", 7 ) ) {
+		process_string( data+7, nref, p );
+		return 0;
+        } else {
+		process_cite( bibin, data, filename, nref, p );
+		return 1;
+	}
+}
+
+/*****************************************************
+ PUBLIC: void biblatexin_cleanf()
+*****************************************************/
+
+static void
+biblatex_process_tilde( str *s )
+{
+	char *p, *q;
+	int n = 0;
+
+	p = q = s->data;
+	if ( !p ) return;
+	while ( *p ) {
+		if ( *p=='~' ) {
+			*q = ' ';
+		} else if ( *p=='\\' && *(p+1)=='~' ) {
+			n++;
+			p++;
+			*q = '~';
+		} else {
+			*q = *p;
+		}
+		p++;
+		q++;
+	}
+	*q = '\0';
+	s->len -= n;
+}
+
+static void
+biblatex_process_bracket( str *s )
+{
+	char *p, *q;
+	int n = 0;
+
+	p = q = s->data;
+	if ( !p ) return;
+	while ( *p ) {
+		if ( *p=='\\' && ( *(p+1)=='{' || *(p+1)=='}' ) ) {
+			n++;
+			p++;
+			*q = *p;
+			q++;
+		} else if ( *p=='{' || *p=='}' ) {
+			n++;
+		} else {
+			*q = *p;
+			q++;
+		}
+		p++;
+	}
+	*q = '\0';
+	s->len -= n;
+}
+
+static int
+biblatex_cleantoken( str *s )
+{
+	/* 'textcomp' annotations */
+	str_findreplace( s, "\\textit", "" );
+	str_findreplace( s, "\\textbf", "" );
+	str_findreplace( s, "\\textsl", "" );
+	str_findreplace( s, "\\textsc", "" );
+	str_findreplace( s, "\\textsf", "" );
+	str_findreplace( s, "\\texttt", "" );
+	str_findreplace( s, "\\textsubscript", "" );
+	str_findreplace( s, "\\textsuperscript", "" );
+	str_findreplace( s, "\\emph", "" );
+	str_findreplace( s, "\\url", "" );
+
+	/* Other text annotations */
+	str_findreplace( s, "\\it ", "" );
+	str_findreplace( s, "\\em ", "" );
+
+	str_findreplace( s, "\\%", "%" );
+	str_findreplace( s, "\\$", "$" );
+	while ( str_findreplace( s, "  ", " " ) ) {}
+
+	/* 'textcomp' annotations that we don't want to substitute on output*/
+	str_findreplace( s, "\\textdollar", "$" );
+	str_findreplace( s, "\\textunderscore", "_" );
+
+	biblatex_process_bracket( s );
+	biblatex_process_tilde( s );
+
+	if ( !str_memerr( s ) ) return BIBL_OK;
+	else return BIBL_ERR_MEMERR;
+}
+
+static int
+biblatex_split( slist *tokens, str *s )
+{
+	int i, n = s->len, nbrackets = 0, status = BIBL_OK;
+	str tok, *t;
+
+	str_init( &tok );
+
+	for ( i=0; i<n; ++i ) {
+		if ( s->data[i]=='{' && ( i==0 || s->data[i-1]!='\\' ) ) {
+			nbrackets++;
+			str_addchar( &tok, '{' );
+		} else if ( s->data[i]=='}' && ( i==0 || s->data[i-1]!='\\' ) ) {
+			nbrackets--;
+			str_addchar( &tok, '}' );
+		} else if ( !is_ws( s->data[i] ) || nbrackets ) {
+			str_addchar( &tok, s->data[i] );
+		} 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; }
+			}
+			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; }
+	}
+
+	for ( i=0; i<tokens->n; ++i ) {
+		t = slist_str( tokens, i );
+		str_trimstartingws( t );
+		str_trimendingws( t );
+		if ( str_memerr( t ) ) { status = BIBL_ERR_MEMERR; goto out; }
+	}
+out:
+	str_free( &tok );
+	return status;
+}
+
+static int
+biblatexin_addtitleurl( fields *info, str *in )
+{
+	int fstatus, status = BIBL_OK;
+	char *p;
+	str s;
+
+	str_init( &s );
+	/* skip past "\href{" */
+	p = str_cpytodelim( &s, in->data + 6, "}", 1 );
+	if ( str_memerr( &s ) ) {
+		status = BIBL_ERR_MEMERR;
+		goto out;
+	}
+	fstatus = fields_add( info, "URL", s.data, 0 );
+	if ( fstatus!=FIELDS_OK ) {
+		status = BIBL_ERR_MEMERR;
+		goto out;
+	}
+
+	p = str_cpytodelim( &s, p, "", 0 );
+	if ( str_memerr( &s ) ) {
+		status = BIBL_ERR_MEMERR;
+		goto out;
+	}
+	str_swapstrings( &s, in );
+out:
+	str_free( &s );
+	return status;
+}
+
+static int
+is_name_tag( str *tag )
+{
+	if ( str_has_value( tag ) ) {
+		if ( !strcasecmp( str_cstr( tag ), "author" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "editor" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "editorb" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "editorc" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "director" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "producer" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "execproducer" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "writer" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "redactor" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "annotator" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "commentator" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "translator" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "foreword" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "afterword" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "introduction" ) ) return 1;
+	}
+	return 0;
+}
+
+static int
+is_url_tag( str *tag )
+{
+	if ( str_has_value( tag ) ) {
+		if ( !strcasecmp( str_cstr( tag ), "url" ) ) return 1;
+	}
+	return 0;
+}
+
+static int
+biblatexin_cleandata( str *tag, str *s, fields *info, param *p )
+{
+	slist tokens;
+	str *tok;
+	int i, status = BIBL_OK;
+	if ( str_is_empty( s ) ) return status;
+	/* protect url from undergoing any parsing */
+	if ( is_url_tag( tag ) ) return status;
+	slist_init( &tokens );
+	biblatex_split( &tokens, s );
+	for ( i=0; i<tokens.n; ++i ) {
+		if ( !strncasecmp(slist_cstr( &tokens, i ), "\\href{", 6 )) {
+			status = biblatexin_addtitleurl( info, slist_str( &tokens, i ) );
+			if ( status!=BIBL_OK ) goto out;
+		}
+		if ( p && p->latexin && !is_name_tag( tag ) ) {
+			status = biblatex_cleantoken( slist_str( &tokens, i ) );
+			if ( status!=BIBL_OK ) goto out;
+		}
+	}
+	str_empty( s );
+	for ( i=0; i<tokens.n; ++i ) {
+		tok = slist_str( &tokens, i );
+		if ( i>0 ) str_addchar( s, ' ' );
+		str_strcat( s, tok );
+	}
+out:
+	slist_free( &tokens );
+	return status;
+}
+
+static long
+biblatexin_findref( bibl *bin, char *citekey )
+{
+	int n;
+	long i;
+	for ( i=0; i<bin->nrefs; ++i ) {
+		n = fields_find( bin->ref[i], "refnum", -1 );
+		if ( n==-1 ) continue;
+		if ( !strcmp( bin->ref[i]->data[n].data, citekey ) ) return i;
+	}
+	return -1;
+}
+
+static void
+biblatexin_nocrossref( bibl *bin, long i, int n, param *p )
+{
+	int n1 = fields_find( bin->ref[i], "REFNUM", -1 );
+	if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
+	fprintf( stderr, "Cannot find cross-reference '%s'", 
+			bin->ref[i]->data[n].data);
+	if ( n1!=-1 )
+		fprintf( stderr, " for reference '%s'\n", 
+				bin->ref[i]->data[n1].data );
+	fprintf( stderr, "\n" );
+}
+
+static int
+biblatexin_crossref_oneref( fields *ref, fields *cross )
+{
+	int j, nl, ntype, fstatus;
+	char *type, *nt, *nd;
+	ntype = fields_find( ref, "INTERNAL_TYPE", -1 );
+	type = ( char * ) fields_value( ref, ntype, FIELDS_CHRP_NOUSE );
+	for ( j=0; j<cross->n; ++j ) {
+		nt = ( char * ) fields_tag( cross, j, FIELDS_CHRP_NOUSE );
+		if ( !strcasecmp( nt, "INTERNAL_TYPE" ) ) continue;
+		if ( !strcasecmp( nt, "REFNUM" ) ) continue;
+		if ( !strcasecmp( nt, "TITLE" ) ) {
+			if ( !strcasecmp( type, "Inproceedings" ) ||
+			     !strcasecmp( type, "Incollection" ) )
+				nt = "booktitle";
+		}
+		nd = ( char * ) fields_value( cross, j, FIELDS_CHRP_NOUSE );
+		nl = fields_level( cross, j ) + 1;
+		fstatus = fields_add( ref, nt, nd, nl );
+		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+	}
+	return BIBL_OK;
+}
+
+static int
+biblatexin_crossref( bibl *bin, param *p )
+{
+	int n, ncross, status = BIBL_OK;
+	fields *ref, *cross;
+	long i;
+        for ( i=0; i<bin->nrefs; ++i ) {
+		ref = bin->ref[i];
+		n = fields_find( ref, "CROSSREF", -1 );
+		if ( n==-1 ) continue;
+		fields_setused( ref, n );
+		ncross = biblatexin_findref(bin, (char*)fields_value(ref,n, FIELDS_CHRP_NOUSE));
+		if ( ncross==-1 ) {
+			biblatexin_nocrossref( bin, i, n, p );
+			continue;
+		}
+		cross = bin->ref[ncross];
+		status = biblatexin_crossref_oneref( ref, cross );
+		if ( status!=BIBL_OK ) return status;
+	}
+	return status;
+}
+
+static int
+biblatexin_cleanref( fields *bibin, param *p )
+{
+	int i, n, status;
+	str *t, *d;
+	n = fields_num( bibin );
+	for ( i=0; i<n; ++i ) {
+		t = fields_tag( bibin, i, FIELDS_STRP_NOUSE );
+		d = fields_value( bibin, i, FIELDS_STRP_NOUSE );
+		status = biblatexin_cleandata( t, d, bibin, p );
+		if ( status!=BIBL_OK ) return status;
+		if ( !strsearch( str_cstr( t ), "AUTHORS" ) ) {
+			str_findreplace( d, "\n", " " );
+			str_findreplace( d, "\r", " " );
+		}
+		else if ( !strsearch( str_cstr( t ), "ABSTRACT" ) ||
+		     !strsearch( str_cstr( t ), "SUMMARY" ) || 
+		     !strsearch( str_cstr( t ), "NOTE" ) ) {
+			str_findreplace( d, "\n", "" );
+			str_findreplace( d, "\r", "" );
+		}
+	}
+	return BIBL_OK;
+}
+
+static int
+biblatexin_cleanf( bibl *bin, param *p )
+{
+	int status;
+	long i;
+        for ( i=0; i<bin->nrefs; ++i ) {
+		status = biblatexin_cleanref( bin->ref[i], p );
+		if ( status!=BIBL_OK ) return status;
+	}
+	status = biblatexin_crossref( bin, p );
+	return status;
+}
+
+/*****************************************************
+ PUBLIC: void biblatexin_typef()
+*****************************************************/
+
+static int
+biblatexin_typef( fields *bibin, char *filename, int nrefs, param *p )
+{
+	int ntypename, nrefname, is_default;
+	char *refname = "", *typename="";
+
+	ntypename = fields_find( bibin, "INTERNAL_TYPE", LEVEL_MAIN );
+	nrefname  = fields_find( bibin, "REFNUM",        LEVEL_MAIN );
+	if ( nrefname!=-1 )  refname  = fields_value( bibin, nrefname,  FIELDS_CHRP_NOUSE );
+        if ( ntypename!=-1 ) typename = fields_value( bibin, ntypename, FIELDS_CHRP_NOUSE );
+
+	return get_reftype( typename, nrefs, p->progname, p->all, p->nall, refname, &is_default, REFTYPE_CHATTY );
+}
+
+/*****************************************************
+ PUBLIC: int biblatexin_convertf(), returns BIBL_OK or BIBL_ERR_MEMERR
+*****************************************************/
+
+/* get_title_elements()
+ *
+ * find all of the biblatex title elements for the current level
+ *    internal "TITLE"      -> "title", "booktitle", "maintitle"
+ *    internal "SUBTITLE"   -> "subtitle", "booksubtitle", "mainsubtitle"
+ *    internal "TITLEADDON" -> "titleaddon", "booktitleaddon", "maintitleaddon"
+ *
+ * place in ttl, subttl, and ttladdon strings
+ *
+ * return 1 if an element is found, 0 if not
+ */
+static int
+get_title_elements( fields *bibin, int currlevel, int reftype, variants *all, int nall, str *ttl, str *subttl, str *ttladdon )
+{
+	int nfields, process, level, i;
+	str *t, *d;
+	char *newtag;
+
+	strs_empty( ttl, subttl, ttladdon, NULL );
+
+	nfields = fields_num( bibin );
+
+	for ( i=0; i<nfields; ++i ) {
+
+		/* ...skip already used titles */
+		if ( fields_used( bibin, i ) ) continue;
+
+		/* ...skip empty elements */
+		t = fields_tag  ( bibin, i, FIELDS_STRP_NOUSE );
+		d = fields_value( bibin, i, FIELDS_STRP_NOUSE );
+		if ( d->len == 0 ) continue;
+
+		if ( !translate_oldtag( t->data, reftype, all, nall, &process, &level, &newtag ) )
+			continue;
+		if ( process != TITLE ) continue;
+		if ( level != currlevel ) continue;
+
+		fields_setused( bibin, i );
+
+		if ( !strcasecmp( newtag, "TITLE" ) ) {
+			if ( str_has_value( ttl ) ) str_addchar( ttl, ' ' );
+			str_strcat( ttl, d );
+		} else if ( !strcasecmp( newtag, "SUBTITLE" ) ) {
+			if ( str_has_value( subttl ) ) str_addchar( subttl, ' ' );
+			str_strcat( subttl, d );
+		} else if ( !strcasecmp( newtag, "TITLEADDON" ) ) {
+			if ( str_has_value( ttladdon ) ) str_addchar( ttladdon, ' ' );
+			str_strcat( ttladdon, d );
+		}
+	}
+
+	return ( ttl->len>0 || subttl->len > 0 || ttladdon->len > 0 );
+}
+
+/* attach_subtitle()
+ *
+ * Add subtitle (if exists) to the title
+ */
+static void
+attach_subtitle( str *title, str *subtitle )
+{
+	if ( str_has_value( subtitle ) ) {
+		if ( str_has_value( title ) ) {
+			if ( title->data[title->len-1]!=':' && title->data[title->len-1]!='?' )
+				str_addchar( title, ':' );
+			str_addchar( title, ' ' );
+		}
+		str_strcat( title, subtitle );
+	}
+}
+
+/* attach_addon()
+ *
+ * Add titleaddon (if exists) to the title.
+ */
+static void
+attach_addon( str *title, str *addon )
+{
+	if ( str_has_value( addon ) ) {
+		if ( str_has_value( title ) ) {
+			if ( title->data[title->len-1]!='.' )
+				str_addchar( title, '.' );
+			str_addchar( title, ' ' );
+		}
+		str_strcat( title, addon );
+	}
+}
+
+static int
+process_combined_title( fields *info, str *ttl, str *subttl, str *ttladdon, int currlevel )
+{
+	int fstatus, status = BIBL_OK;
+	str combined;
+
+	str_init( &combined );
+
+	str_strcpy( &combined, ttl );
+	attach_subtitle( &combined, subttl );
+	attach_addon( &combined, ttladdon );
+
+	if ( str_memerr( &combined ) ) {
+		status = BIBL_ERR_MEMERR;
+		goto out;
+	}
+
+	fstatus = fields_add( info, "TITLE", combined.data, currlevel );
+	if ( fstatus==FIELDS_OK ) status = BIBL_ERR_MEMERR;
+out:
+	str_free( &combined );
+	return status;
+}
+
+static int
+process_separated_title( fields *info, str *ttl, str *subttl, str *ttladdon, int currlevel )
+{
+	int fstatus;
+	if ( str_has_value( ttladdon ) ) {
+		if ( subttl->len ) attach_addon( subttl, ttladdon );
+		else attach_addon( ttl, ttladdon );
+	}
+	if ( str_has_value( ttl ) ) {
+		fstatus = fields_add( info, "TITLE", str_cstr( ttl ), currlevel );
+		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+	}
+	if ( str_has_value( subttl ) ) {
+		fstatus = fields_add( info, "SUBTITLE", str_cstr( subttl ), currlevel );
+		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+	}
+	return BIBL_OK;
+}
+
+static int
+process_title_all( fields *bibin, fields *info, int reftype, param *p )
+{
+	int currlevel, found, status = BIBL_OK;
+	str ttl, subttl, ttladdon;
+	strs_init( &ttl, &subttl, &ttladdon, NULL );
+	for ( currlevel = 0; currlevel<LEVEL_SERIES+2; currlevel++ ) {
+		found = get_title_elements( bibin, currlevel, reftype, p->all, p->nall, &ttl, &subttl, &ttladdon );
+		if ( !found ) continue;
+		if ( p->nosplittitle )
+			status = process_combined_title( info, &ttl, &subttl, &ttladdon, currlevel );
+		else
+			status = process_separated_title( info, &ttl, &subttl, &ttladdon, currlevel );
+		if ( status!=BIBL_OK ) goto out;
+	}
+out:
+	strs_free( &ttl, &subttl, &ttladdon, NULL );
+	return status;
+}
+
+
+static int
+biblatex_matches_list( fields *info, char *tag, char *suffix, str *data, int level, slist *names, int *match )
+{
+	int i, fstatus, status = BIBL_OK;
+	str newtag;
+
+	*match = 0;
+	if ( names->n==0 ) return status;
+
+	str_init( &newtag );
+
+	for ( i=0; i<names->n; ++i ) {
+		if ( strcmp( str_cstr( data ), slist_cstr( names, i ) ) ) continue;
+		str_initstrc( &newtag, tag );
+		str_strcatc( &newtag, suffix );
+		fstatus = fields_add( info, str_cstr( &newtag ), str_cstr( data ), level );
+		if ( fstatus!=FIELDS_OK ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+		*match = 1;
+		goto out;
+	}
+
+out:
+	str_free( &newtag );
+	return status;
+}
+
+static int
+biblatex_names( fields *info, char *tag, str *data, int level, slist *asis, slist *corps )
+{
+	int begin, end, ok, n, etal, i, match, status = BIBL_OK;
+	slist tokens;
+
+	/* If we match the asis or corps list add and bail. */
+	status = biblatex_matches_list( info, tag, ":ASIS", data, level, asis, &match );
+	if ( match==1 || status!=BIBL_OK ) return status;
+	status = biblatex_matches_list( info, tag, ":CORP", data, level, corps, &match );
+	if ( match==1 || status!=BIBL_OK ) return status;
+
+	slist_init( &tokens );
+
+	biblatex_split( &tokens, data );
+	for ( i=0; i<tokens.n; ++i )
+		biblatex_cleantoken( slist_str( &tokens, i ) );
+
+	etal = name_findetal( &tokens );
+
+	begin = 0;
+	n = tokens.n - etal;
+	while ( begin < n ) {
+
+		end = begin + 1;
+
+		while ( end < n && strcasecmp( slist_cstr( &tokens, end ), "and" ) )
+			end++;
+
+		if ( end - begin == 1 ) {
+			ok = name_addsingleelement( info, tag, slist_cstr( &tokens, begin ), level, 0 );
+			if ( !ok ) { status = BIBL_ERR_MEMERR; goto out; }
+		} else {
+			ok = name_addmultielement( info, tag, &tokens, begin, end, level );
+			if ( !ok ) { status = BIBL_ERR_MEMERR; goto out; }
+		}
+
+		begin = end + 1;
+
+		/* Handle repeated 'and' errors */
+		while ( begin < n && !strcasecmp( slist_cstr( &tokens, begin ), "and" ) )
+			begin++;
+
+	}
+
+	if ( etal ) {
+		ok = name_addsingleelement( info, tag, "et al.", level, 0 );
+		if ( !ok ) status = BIBL_ERR_MEMERR;
+	}
+
+out:
+	slist_free( &tokens );
+	return status;
+}
+
+static int
+biblatexin_bltsubtype( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int fstatus1, fstatus2;
+
+	if ( !strcasecmp( str_cstr( invalue ), "magazine" ) ) {
+		fstatus1 = fields_add( bibout, "NGENRE", "magazine article", LEVEL_MAIN );
+		fstatus2 = fields_add( bibout, "NGENRE", "magazine",         LEVEL_HOST );
+		if ( fstatus1!=FIELDS_OK || fstatus2!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+	}
+
+	else if ( !strcasecmp( str_cstr( invalue ), "newspaper" ) ) {
+		fstatus1 = fields_add( bibout, "NGENRE", "newspaper article", LEVEL_MAIN );
+		fstatus2 = fields_add( bibout, "GENRE",  "newspaper",         LEVEL_HOST );
+		if ( fstatus1!=FIELDS_OK || fstatus2!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+	}
+
+	return BIBL_OK;
+}
+
+/* biblatex drops school field if institution is present */
+static int
+biblatexin_bltschool( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int fstatus;
+	if ( fields_find( bibin, "institution", LEVEL_ANY ) != -1 )
+		return BIBL_OK;
+	else {
+		fstatus = fields_add( bibout, outtag, str_cstr( invalue ), level );
+		if ( fstatus==FIELDS_OK ) return BIBL_OK;
+		else return BIBL_ERR_MEMERR;
+	}
+}
+
+static int
+biblatexin_bltthesistype( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	char *p = invalue->data;
+	int fstatus = FIELDS_OK;
+	/* type in the @thesis is used to distinguish Ph.D. and Master's thesis */
+	if ( !strncasecmp( p, "phdthesis", 9 ) ) {
+		fstatus = fields_replace_or_add( bibout, "NGENRE", "Ph.D. thesis", level );
+	} else if ( !strncasecmp( p, "mastersthesis", 13 ) || !strncasecmp( p, "masterthesis", 12 ) ) {
+		fstatus = fields_replace_or_add( bibout, "NGENRE", "Masters thesis", level );
+	} else if ( !strncasecmp( p, "mathesis", 8 ) ) {
+		fstatus = fields_replace_or_add( bibout, "NGENRE", "Masters thesis", level );
+	} else if ( !strncasecmp( p, "diploma", 7 ) ) {
+		fstatus = fields_replace_or_add( bibout, "NGENRE", "Diploma thesis", level );
+	} else if ( !strncasecmp( p, "habilitation", 12 ) ) {
+		fstatus = fields_replace_or_add( bibout, "NGENRE", "Habilitation thesis", level );
+	}
+	if ( fstatus==FIELDS_OK ) return BIBL_OK;
+	else return BIBL_ERR_MEMERR;
+}
+
+static int
+biblatexin_bteprint( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int neprint, netype, fstatus;
+	char *eprint = NULL, *etype = NULL;
+
+	neprint = fields_find( bibin, "eprint", -1 );
+	netype  = fields_find( bibin, "eprinttype", -1 );
+
+	if ( neprint!=-1 ) eprint = bibin->data[neprint].data;
+	if ( netype!=-1 )  etype =  bibin->data[netype].data;
+
+	if ( eprint && etype ) {
+		if ( !strncasecmp( etype, "arxiv", 5 ) ) {
+			fstatus = fields_add( bibout, "ARXIV", eprint, level );
+			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+		} else if ( !strncasecmp( etype, "jstor", 5 ) ) {
+			fstatus = fields_add( bibout, "JSTOR", eprint, level );
+			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+		} else if ( !strncasecmp( etype, "pubmed", 6 ) ) {
+			fstatus = fields_add( bibout, "PMID", eprint, level );
+			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+		} else if ( !strncasecmp( etype, "medline", 7 ) ) {
+			fstatus = fields_add( bibout, "MEDLINE", eprint, level );
+			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+		} else {
+			fstatus = fields_add( bibout, "EPRINT", eprint, level );
+			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+			fstatus = fields_add( bibout, "EPRINTTYPE", etype, level );
+			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+		}
+		fields_setused( bibin, neprint );
+		fields_setused( bibin, netype );
+	} else if ( eprint ) {
+		fstatus = fields_add( bibout, "EPRINT", eprint, level );
+		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+		fields_setused( bibin, neprint );
+	} else if ( etype ) {
+		fstatus = fields_add( bibout, "EPRINTTYPE", etype, level );
+		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+		fields_setused( bibin, netype );
+	}
+	return BIBL_OK;
+}
+
+static int
+biblatexin_btgenre( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	if ( fields_add( bibout, "NGENRE", str_cstr( invalue ), level ) == FIELDS_OK ) return BIBL_OK;
+	else return BIBL_ERR_MEMERR;
+}
+
+/* biblatexin_howpublished()
+ *
+ *    howpublished={},
+ *
+ * Normally indicates the manner in which something was
+ * published in lieu of a formal publisher, so typically
+ * 'howpublished' and 'publisher' will never be in the
+ * same reference.
+ *
+ * Occasionally, people put Diploma thesis information
+ * into this field, so check for that first.
+ */
+static int
+biblatexin_howpublished( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int fstatus;
+
+        if ( !strncasecmp( str_cstr( invalue ), "Diplom", 6 ) )
+                fstatus = fields_replace_or_add( bibout, "NGENRE", "Diploma thesis", level );
+        else if ( !strncasecmp( str_cstr( invalue ), "Habilitation", 13 ) )
+                fstatus = fields_replace_or_add( bibout, "NGENRE", "Habilitation thesis", level );
+        else
+		fstatus = fields_add( bibout, "PUBLISHER", str_cstr( invalue ), level );
+
+	if ( fstatus==FIELDS_OK ) return BIBL_OK;
+	else return BIBL_ERR_MEMERR;
+}
+
+/*
+ * biblatex has multiple editor fields "editor", "editora", "editorb", "editorc",
+ * each of which can be modified from a type of "EDITOR" via "editortype",
+ * "editoratype", "editorbtype", "editorctype".
+ *
+ * Defined types:
+ *     "editor"
+ *     "collaborator"
+ *     "compiler"
+ *     "redactor"
+ *
+ *     "reviser" ?
+ *     "founder" ?
+ *     "continuator" ?
+ *
+ *  bibtex-chicago
+ *
+ *     "director"
+ *     "producer"
+ *     "conductor"
+ *     "none" (for performer)
+ */
+static int
+biblatexin_blteditor( fields *bibin, int m, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	char *editor_fields[] = { "editor", "editora", "editorb", "editorc" };
+	char *editor_types[]  = { "editortype", "editoratype", "editorbtype", "editorctype" };
+	int i, n = 0, ntype, neditors = sizeof( editor_fields ) / sizeof( editor_fields[0] );
+	char *type, *usetag = "EDITOR";
+	for ( i=1; i<neditors; ++i )
+		if ( !strcasecmp( intag->data, editor_fields[i] ) ) n = i;
+	ntype = fields_find( bibin, editor_types[n], LEVEL_ANY );
+	if ( ntype!=-1 ) {
+		type = fields_value( bibin, ntype, FIELDS_CHRP_NOUSE );
+		if ( !strcasecmp( type, "collaborator" ) )  usetag = "COLLABORATOR";
+		else if ( !strcasecmp( type, "compiler" ) ) usetag = "COMPILER";
+		else if ( !strcasecmp( type, "redactor" ) ) usetag = "REDACTOR";
+		else if ( !strcasecmp( type, "director" ) ) usetag = "DIRECTOR";
+		else if ( !strcasecmp( type, "producer" ) ) usetag = "PRODUCER";
+		else if ( !strcasecmp( type, "none" ) )     usetag = "PERFORMER";
+	}
+	return biblatex_names( bibout, usetag, invalue, level, &(pm->asis), &(pm->corps) );
+}
+
+static int
+biblatexin_person( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	return biblatex_names( bibout, outtag, invalue, level, &(pm->asis), &(pm->corps) );
+}
+
+static void
+biblatexin_notag( param *p, char *tag )
+{
+	if ( p->verbose && strcmp( tag, "INTERNAL_TYPE" ) ) {
+		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
+		fprintf( stderr, " Cannot find tag '%s'\n", tag );
+	}
+}
+
+static int
+biblatexin_convertf( fields *bibin, fields *bibout, int reftype, param *p )
+{
+	static int (*convertfns[NUM_REFTYPES])(fields *, int, str *, str *, int, param *, char *, fields *) = {
+		[ 0 ... NUM_REFTYPES-1 ] = generic_null,
+		[ SIMPLE          ] = generic_simple,
+		[ PAGES           ] = generic_pages,
+		[ NOTES           ] = generic_notes,
+		[ PERSON          ] = biblatexin_person,
+		[ BLT_EDITOR      ] = biblatexin_blteditor,
+		[ HOWPUBLISHED    ] = biblatexin_howpublished,
+		[ URL             ] = generic_url,
+		[ BT_GENRE        ] = biblatexin_btgenre,
+		[ BT_EPRINT       ] = biblatexin_bteprint,
+		[ BLT_THESIS_TYPE ] = biblatexin_bltthesistype,
+		[ BLT_SCHOOL      ] = biblatexin_bltschool,
+		[ BLT_SUBTYPE     ] = biblatexin_bltsubtype,
+		[ BLT_SKIP        ] = generic_skip,
+		[ TITLE           ] = generic_null,    /* delay processing until later */
+	};
+
+	int process, level, i, nfields, status = BIBL_OK;
+	str *intag, *invalue;
+	char *outtag;
+
+	nfields = fields_num( bibin );
+	for ( i=0; i<nfields; ++i ) {
+
+               /* skip ones already "used" such as successful crossref */
+                if ( fields_used( bibin, i ) ) continue;
+
+		/* skip ones with no data or no tags (e.g. don't match ALWAYS/DEFAULT entries) */
+		intag   = fields_tag  ( bibin, i, FIELDS_STRP_NOUSE );
+		invalue = fields_value( bibin, i, FIELDS_STRP_NOUSE );
+		if ( str_is_empty( intag ) || str_is_empty( invalue ) ) continue;
+
+		if ( !translate_oldtag( intag->data, reftype, p->all, p->nall, &process, &level, &outtag ) ) {
+			biblatexin_notag( p, intag->data );
+			continue;
+		}
+
+		status = convertfns[ process ]( bibin, i, intag, invalue, level, p, outtag, bibout );
+		if ( status!=BIBL_OK ) return status;
+
+		if ( convertfns[ process ] != generic_null )
+			fields_setused( bibin, i );
+
+	}
+
+	status = process_title_all( bibin, bibout, reftype, p );
+
+	if ( status==BIBL_OK && p->verbose ) fields_report( bibout, stdout );
 
 	return status;
 }
diff --git a/bibutils/biblatexin.h b/bibutils/biblatexin.h
deleted file mode 100644
--- a/bibutils/biblatexin.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * biblatexin.h
- *
- * Copyright (c) Chris Putnam 2008-2014
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef BIBLATEXIN_H
-#define BIBLATEXIN_H
-
-#include "newstr.h"
-#include "list.h"
-#include "fields.h"
-#include "bibl.h"
-#include "bibutils.h"
-#include "reftypes.h"
-
-extern int  biblatexin_convertf( fields *bibin, fields *info, int reftype, param *p, variants *all, int nall );
-extern int  biblatexin_processf( fields *bibin, char *data, char *filename, long nref );
-extern int  biblatexin_cleanf( bibl *bin, param *p );
-extern int  biblatexin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset );
-extern int  biblatexin_typef( fields *bibin, char *filename, int nrefs,
-        param *p, variants *all, int nall );
-
-extern void biblatexin_initparams( param *p, const char *progname );
-
-extern variants biblatex_all[];
-extern int biblatex_nall;
-
-
-#endif
-
diff --git a/bibutils/bibtexin.c b/bibutils/bibtexin.c
--- a/bibutils/bibtexin.c
+++ b/bibutils/bibtexin.c
@@ -1,1375 +1,1325 @@
 /*
  * bibtexin.c
  *
- * Copyright (c) Chris Putnam 2003-2014
- *
- * Program and source code released under the GPL version 2
- *
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include "is_ws.h"
-#include "strsearch.h"
-#include "newstr.h"
-#include "newstr_conv.h"
-#include "fields.h"
-#include "list.h"
-#include "name.h"
-#include "title.h"
-#include "reftypes.h"
-#include "bibtexin.h"
-
-static list find    = { 0, 0, NULL, 0 };
-static list replace = { 0, 0, NULL, 0 };
-
-extern const char progname[];
-
-/*****************************************************
- PUBLIC: void bibtexin_initparams()
-*****************************************************/
-void
-bibtexin_initparams( param *p, 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;
-
-	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;
-
-	list_init( &(p->asis) );
-	list_init( &(p->corps) );
-
-	if ( !progname ) p->progname = NULL;
-	else p->progname = strdup( progname );
-}
-
-/*****************************************************
- PUBLIC: int bibtexin_readf()
-*****************************************************/
-
-/*
- * readf can "read too far", so we store this information in line, thus
- * the next new text is in line, either from having read too far or
- * from the next chunk obtained via newstr_fget()
- *
- * return 1 on success, 0 on error/end-of-file
- *
- */
-static int
-readmore( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line )
-{
-	if ( line->len ) return 1;
-	else return newstr_fget( fp, buf, bufsize, bufpos, line );
-}
-
-/*
- * readf()
- *
- * returns zero if cannot get reference and hit end of-file
- * returns 1 if last reference in file, 2 if reference within file
- */
-int
-bibtexin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset )
-{
-	int haveref = 0;
-	char *p;
-	*fcharset = CHARSET_UNKNOWN;
-	while ( haveref!=2 && readmore( fp, buf, bufsize, bufpos, line ) ) {
-		if ( line->len == 0 ) continue; /* blank line */
-		p = &(line->data[0]);
-		/* Recognize UTF8 BOM */
-		if ( line->len > 2 && 
-				(unsigned char)(p[0])==0xEF &&
-				(unsigned char)(p[1])==0xBB &&
-				(unsigned char)(p[2])==0xBF ) {
-			*fcharset = CHARSET_UNICODE;
-			p += 3;
-		}
-		p = skip_ws( p );
-		if ( *p == '%' ) { /* commented out line */
-			newstr_empty( line );
-			continue;
-		}
-		if ( *p == '@' ) haveref++;
-		if ( haveref && haveref<2 ) {
-			newstr_strcat( reference, p );
-			newstr_addchar( reference, '\n' );
-			newstr_empty( line );
-		} else if ( !haveref ) newstr_empty( line );
-	
-	}
-	return haveref;
-}
-
-/*****************************************************
- PUBLIC: int bibtexin_processf()
-*****************************************************/
-
-static char*
-process_bibtextype( char *p, newstr *type )
-{
-	newstr tmp;
-	newstr_init( &tmp );
-
-	if ( *p=='@' ) p++;
-	p = newstr_cpytodelim( &tmp, p, "{( \t\r\n", 0 );
-	p = skip_ws( p );
-	if ( *p=='{' || *p=='(' ) p++;
-	p = skip_ws( p );
-
-	if ( tmp.len ) newstr_strcpy( type, tmp.data );
-	else newstr_empty( type );
-
-	newstr_free( &tmp );
-	return p;
-}
-
-static char*
-process_bibtexid( char *p, newstr *id )
-{
-	char *start_p = p;
-	newstr tmp;
-
-	newstr_init( &tmp );
-	p = newstr_cpytodelim( &tmp, p, ",", 1 );
-
-	if ( tmp.len ) {
-		if ( strchr( tmp.data, '=' ) ) {
-			/* Endnote writes bibtex files w/o fields, try to
-			 * distinguish via presence of an equal sign.... if
-			 * it's there, assume that it's a tag/data pair instead
-			 * and roll back.
-			 */
-			p = start_p;
-			newstr_empty( id );
-		} else {
-			newstr_strcpy( id, tmp.data );
-		}
-	} else {
-		newstr_empty( id );
-	}
-
-	newstr_free( &tmp );
-	return skip_ws( p );
-}
-
-static char *
-bibtex_tag( char *p, newstr *tag )
-{
-	p = newstr_cpytodelim( tag, skip_ws( p ), "= \t\r\n", 0 );
-	if ( newstr_memerr( tag ) ) return NULL;
-	return skip_ws( p );
-}
-
-static char *
-bibtex_data( char *p, fields *bibin, list *tokens )
-{
-	unsigned int nbracket = 0, nquotes = 0;
-	char *startp = p;
-	newstr tok, *t;
-
-	newstr_init( &tok );
-	while ( p && *p ) {
-		if ( !nquotes && !nbracket ) {
-			if ( *p==',' || *p=='=' || *p=='}' || *p==')' )
-				goto out;
-		}
-		if ( *p=='\"' && nbracket==0 && ( p==startp || *(p-1)!='\\' ) ) {
-			nquotes = !nquotes;
-			newstr_addchar( &tok, *p );
-			if ( !nquotes ) {
-				if ( newstr_memerr( &tok ) ) { p=NULL; goto out; }
-				t = list_add( tokens, &tok );
-				if ( !t ) { p=NULL; goto out0; }
-				newstr_empty( &tok );
-			}
-		} else if ( *p=='#' && !nquotes && !nbracket ) {
-			if ( tok.len ) {
-				if ( newstr_memerr( &tok ) ) { p=NULL; goto out; }
-				t = list_add( tokens, &tok );
-				if ( !t ) { p=NULL; goto out0; }
-			}
-			newstr_strcpy( &tok, "#" );
-			t = list_add( tokens, &tok );
-			if ( !t ) { p=NULL; goto out0; }
-			newstr_empty( &tok );
-		} else if ( *p=='{' && !nquotes && ( p==startp || *(p-1)!='\\' ) ) {
-			nbracket++;
-			newstr_addchar( &tok, *p );
-		} else if ( *p=='}' && !nquotes && ( p==startp || *(p-1)!='\\' ) ) {
-			nbracket--;
-			newstr_addchar( &tok, *p );
-			if ( nbracket==0 ) {
-				if ( newstr_memerr( &tok ) ) { p=NULL; goto out; }
-				t = list_add( tokens, &tok );
-				if ( !t ) { p=NULL; goto out; }
-				newstr_empty( &tok );
-			}
-		} else if ( !is_ws( *p ) || nquotes || nbracket ) {
-			if ( !is_ws( *p ) ) newstr_addchar( &tok, *p );
-			else {
-				if ( tok.len!=0 && *p!='\n' && *p!='\r' )
-					newstr_addchar( &tok, *p );
-				else if ( tok.len!=0 && (*p=='\n' || *p=='\r')) {
-					newstr_addchar( &tok, ' ' );
-					while ( is_ws( *(p+1) ) ) p++;
-				}
-			}
-		} else if ( is_ws( *p ) ) {
-			if ( tok.len ) {
-				if ( newstr_memerr( &tok ) ) { p=NULL; goto out; }
-				t = list_add( tokens, &tok );
-				if ( !t ) { p=NULL; goto out; }
-				newstr_empty( &tok );
-			}
-		}
-		p++;
-	}
-out:
-	if ( nbracket!=0 ) {
-		fprintf( stderr, "%s: Mismatch in number of brackets in reference.\n", progname );
-	}
-	if ( nquotes!=0 ) {
-		fprintf( stderr, "%s: Mismatch in number of quotes in reference.\n", progname );
-	}
-	if ( tok.len ) {
-		if ( newstr_memerr( &tok ) ) { p = NULL; goto out; }
-		t = list_add( tokens, &tok );
-		if ( !t ) p = NULL;
-	}
-out0:
-	newstr_free( &tok );
-	return p;
-}
-
-/* replace_strings()
- *
- * do string replacement -- only if unprotected by quotation marks or curly brackets
- */
-static void
-replace_strings( list *tokens, fields *bibin )
-{
-	int i, n, ok;
-	newstr *s;
-	char *q;
-	i = 0;
-	while ( i < tokens->n ) {
-		s = list_get( tokens, i );
-		if ( !strcmp( s->data, "#" ) ) {
-		} else if ( s->data[0]!='\"' && s->data[0]!='{' ) {
-			n = list_find( &find, s->data );
-			if ( n!=-1 ) {
-				newstr_newstrcpy( s, list_get( &replace, n ) );
-			} else {
-				q = s->data;
-				ok = 1;
-				while ( *q && ok ) {
-					if ( !isdigit( *q ) ) ok = 0;
-					q++;
-				}
-				if ( !ok ) {
-					fprintf( stderr, "%s: Warning: Non-numeric "
-					   "BibTeX elements should be in quotations or "
-					   "curly brackets in reference.\n", progname );
-				}
-			}
-		}
-		i++;
-	}
-}
-
-static int
-string_concatenate( list *tokens, fields *bibin )
-{
-	newstr *s, *t;
-	int i, status;
-	i = 0;
-	while ( i < tokens->n ) {
-		s = list_get( tokens, i );
-		if ( !strcmp( s->data, "#" ) ) {
-			if ( i==0 || i==tokens->n-1 ) {
-				fprintf( stderr, "%s: Warning: Stray string concatenation "
-					"('#' character) in reference\n", progname );
-				status = list_remove( tokens, i );
-				if ( status!=LIST_OK ) return BIBL_ERR_MEMERR;
-				continue;
-			}
-			s = list_get( 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.\n", progname );
-			t = list_get( 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.\n", progname );
-			if ( ( s->data[s->len-1]=='\"' && t->data[0]=='\"') || (s->data[s->len-1]=='}' && t->data[0]=='{') ) {
-				newstr_trimend( s, 1 );
-				newstr_trimbegin( t, 1 );
-				newstr_newstrcat( s, t );
-			} else {
-				newstr_newstrcat( s, t );
-			}
-			status = list_remove( tokens, i );
-			if ( status!=LIST_OK ) return BIBL_ERR_MEMERR;
-			status = list_remove( tokens, i );
-			if ( status!=LIST_OK ) return BIBL_ERR_MEMERR;
-		} else i++;
-	}
-	return BIBL_OK;
-}
-
-/* return NULL on memory error */
-static char *
-process_bibtexline( char *p, newstr *tag, newstr *data, uchar stripquotes, fields *bibin )
-{
-	int i, status;
-	list tokens;
-	newstr *s;
-
-	newstr_empty( data );
-
-	p = bibtex_tag( p, tag );
-	if ( p==NULL || tag->len==0 ) return p;
-
-	list_init( &tokens );
-
-	if ( *p=='=' ) {
-		p = bibtex_data( p+1, bibin, &tokens );
-		if ( p==NULL ) goto out;
-	}
-
-	replace_strings( &tokens, bibin );
-
-	status = string_concatenate( &tokens, bibin );
-	if ( status!=BIBL_OK ) {
-		p = NULL;
-		goto out;
-	}
-
-	for ( i=0; i<tokens.n; i++ ) {
-		s = list_get( &tokens, i );
-		if ( ( stripquotes && s->data[0]=='\"' && s->data[s->len-1]=='\"' ) ||
-		     ( s->data[0]=='{' && s->data[s->len-1]=='}' ) ) {
-			newstr_trimbegin( s, 1 );
-			newstr_trimend( s, 1 );
-		}
-		newstr_newstrcat( data, list_get( &tokens, i ) );
-	}
-out:
-	list_free( &tokens );
-	return p;
-}
-
-/* process_cite()
- *
- */
-static int
-process_cite( fields *bibin, char *p, char *filename, long nref )
-{
-	int fstatus, status = BIBL_OK;
-	newstr tag, data;
-
-	newstrs_init( &tag, &data, NULL );
-
-	p = process_bibtextype( p, &data );
-	if ( data.len ) {
-		fstatus = fields_add( bibin, "INTERNAL_TYPE", data.data, 0 );
-		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-	}
-
-	p = process_bibtexid( p, &data );
-	if ( data.len ) {
-		fstatus = fields_add( bibin, "REFNUM", data.data, 0 );
-		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-	}
-
-	while ( *p ) {
-		p = process_bibtexline( p, &tag, &data, 1, bibin );
-		if ( p==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
-		/* no anonymous or empty fields allowed */
-		if ( tag.len && data.len ) {
-			fstatus = fields_add( bibin, tag.data, data.data, 0 );
-			if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-		}
-		newstrs_empty( &tag, &data, NULL );
-	}
-out:
-	newstrs_free( &tag, &data, NULL );
-	return status;
-}
-
-/* process_string()
- *
- * Handle lines like:
- *
- * '@STRING{TL = {Tetrahedron Lett.}}'
- *
- * p should point to just after '@STRING'
- *
- * In BibTeX, if a string is defined several times, the last one is kept.
- *
- */
-static int
-process_string( char *p )
-{
-	int n, status = BIBL_OK;
-	newstr s1, s2, *t;
-	newstrs_init( &s1, &s2, NULL );
-	while ( *p && *p!='{' && *p!='(' ) p++;
-	if ( *p=='{' || *p=='(' ) p++;
-	p = process_bibtexline( skip_ws( p ), &s1, &s2, 0, NULL );
-	if ( p==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
-	if ( s2.data ) {
-		newstr_findreplace( &s2, "\\ ", " " );
-	}
-	if ( s1.data ) {
-		n = list_find( &find, s1.data );
-		if ( n==-1 ) {
-			t = list_add( &find, &s1 );
-			if ( t==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
-			if ( s2.data ) t = list_add( &replace, &s2 );
-			else t = list_addc( &replace, "" );
-			if ( t==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
-		} else {
-			if ( s2.data ) t = list_set( &replace, n, &s2 );
-			else t = list_setc( &replace, n, "" );
-			if ( t==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
-		}
-	}
-out:
-	newstrs_free( &s1, &s2, NULL );
-	return status;
-}
-
-/* bibtexin_processf()
- *
- * Handle '@STRING', '@reftype', and ignore '@COMMENT'
- */
-int
-bibtexin_processf( fields *bibin, char *data, char *filename, long nref )
-{
-	if ( !strncasecmp( data, "@STRING", 7 ) ) {
-		process_string( data+7 );
-		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 );
-		return 1;
-	}
-}
-
-/*****************************************************
- PUBLIC: void bibtexin_cleanf()
-*****************************************************/
-
-static int
-bibtex_protected( newstr *data )
-{
-	if ( data->data[0]=='{' && data->data[data->len-1]=='}' ) return 1;
-	if ( data->data[0]=='\"' && data->data[data->len-1]=='\"' ) return 1;
-	return 0;
-}
-
-static int
-bibtex_split( list *tokens, newstr *s )
-{
-	int i, n = s->len, nbrackets = 0, status = BIBL_OK;
-	newstr tok, *t;
-
-	newstr_init( &tok );
-
-	for ( i=0; i<n; ++i ) {
-		if ( s->data[i]=='{' && ( i==0 || s->data[i-1]!='\\' ) ) {
-			nbrackets++;
-			newstr_addchar( &tok, '{' );
-		} else if ( s->data[i]=='}' && ( i==0 || s->data[i-1]!='\\' ) ) {
-			nbrackets--;
-			newstr_addchar( &tok, '}' );
-		} else if ( !is_ws( s->data[i] ) || nbrackets ) {
-			newstr_addchar( &tok, s->data[i] );
-		} else if ( is_ws( s->data[i] ) ) {
-			if ( tok.len ) {
-				t = list_add( tokens, &tok );
-				if ( !t ) {
-					status = BIBL_ERR_MEMERR;
-					goto out;
-				}
-			}
-			newstr_empty( &tok );
-		}
-	}
-	if ( tok.len ) {
-		t = list_add( tokens, &tok );
-		if ( !t ) {
-			status = BIBL_ERR_MEMERR;
-			goto out;
-		}
-	}
-
-	for ( i=0; i<tokens->n; ++i ) {
-		newstr_trimstartingws( list_get( tokens, i ) );
-		newstr_trimendingws( list_get( tokens, i ) );
-	}
-out:
-	newstr_free( &tok );
-	return status;
-}
-
-static int
-bibtex_addtitleurl( fields *info, newstr *in )
-{
-	int fstatus, status = BIBL_OK;
-	newstr s;
-	char *p;
-
-	newstr_init( &s );
-
-	/* ...skip past "\href{" and copy to "}" */
-	p = newstr_cpytodelim( &s, in->data + 6, "}", 1 );
-	if ( newstr_memerr( &s ) ) { status = BIBL_ERR_MEMERR; goto out; }
-
-	/* ...add to URL */
-	fstatus = fields_add( info, "URL", s.data, 0 );
-	if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-
-	/* ...return deleted fragment to newstr in */
-	p = newstr_cpytodelim( &s, p, "", 0 );
-	if ( newstr_memerr( &s ) ) { status = BIBL_ERR_MEMERR; goto out; }
-	newstr_swapstrings( &s, in );
-
-out:
-	newstr_free( &s );
-	return status;
-}
-
-static int
-is_url_tag( newstr *tag )
-{
-	if ( tag->len ) {
-		if ( !strcasecmp( tag->data, "url" ) ) return 1;
-	}
-	return 0;
-}
-
-static int
-is_name_tag( newstr *tag )
-{
-	if ( tag->len ) {
-		if ( !strcasecmp( tag->data, "author" ) ) return 1;
-		if ( !strcasecmp( tag->data, "editor" ) ) return 1;
-	}
-	return 0;
-}
-
-static void
-bibtex_process_tilde( newstr *s )
-{
-	char *p, *q;
-	int n = 0;
-
-	p = q = s->data;
-	if ( !p ) return;
-	while ( *p ) {
-		if ( *p=='~' ) {
-			*q = ' ';
-		} else if ( *p=='\\' && *(p+1)=='~' ) {
-			n++;
-			p++;
-			*q = '~';
-		} else {
-			*q = *p;
-		}
-		p++;
-		q++;
-	}
-	*q = '\0';
-	s->len -= n;
-}
-
-static void
-bibtex_process_bracket( newstr *s )
-{
-	char *p, *q;
-	int n = 0;
-
-	p = q = s->data;
-	if ( !p ) return;
-	while ( *p ) {
-		if ( *p=='\\' && ( *(p+1)=='{' || *(p+1)=='}' ) ) {
-			n++;
-			p++;
-			*q = *p;
-			q++;
-		} else if ( *p=='{' || *p=='}' ) {
-			n++;
-		} else {
-			*q = *p;
-			q++;
-		}
-		p++;
-	}
-	*q = '\0';
-	s->len -= n;
-}
-
-static void
-bibtex_cleantoken( newstr *s )
-{
-	/* 'textcomp' annotations */
-	newstr_findreplace( s, "\\textit", "" );
-	newstr_findreplace( s, "\\textbf", "" );
-	newstr_findreplace( s, "\\textsl", "" );
-	newstr_findreplace( s, "\\textsc", "" );
-	newstr_findreplace( s, "\\textsf", "" );
-	newstr_findreplace( s, "\\texttt", "" );
-	newstr_findreplace( s, "\\textsubscript", "" );
-	newstr_findreplace( s, "\\textsuperscript", "" );
-	newstr_findreplace( s, "\\emph", "" );
-	newstr_findreplace( s, "\\url", "" );
-	newstr_findreplace( s, "\\mbox", "" );
-
-	/* Other text annotations */
-	newstr_findreplace( s, "\\it ", "" );
-	newstr_findreplace( s, "\\em ", "" );
-
-	newstr_findreplace( s, "\\%", "%" );
-	newstr_findreplace( s, "\\$", "$" );
-	while ( newstr_findreplace( s, "  ", " " ) ) {}
-
-	/* 'textcomp' annotations that we don't want to substitute on output*/
-	newstr_findreplace( s, "\\textdollar", "$" );
-	newstr_findreplace( s, "\\textunderscore", "_" );
-
-	bibtex_process_bracket( s );
-	bibtex_process_tilde( s );
-
-}
-
-static int
-bibtex_cleandata( newstr *tag, newstr *s, fields *info, param *p )
-{
-	int i, status;
-	list tokens;
-	newstr *tok;
-	if ( !s->len ) return BIBL_OK;
-	/* protect url from undergoing any parsing */
-	if ( is_url_tag( tag ) ) return BIBL_OK;
-	list_init( &tokens );
-	status = bibtex_split( &tokens, s );
-	if ( status!=BIBL_OK ) goto out;
-	for ( i=0; i<tokens.n; ++i ) {
-		tok = list_get( &tokens, i );
-		if ( bibtex_protected( tok ) ) {
-			if (!strncasecmp(tok->data,"\\href{", 6)) {
-				bibtex_addtitleurl( info, tok );
-			}
-		}
-		if ( p->latexin && !is_name_tag( tag ) && !is_url_tag( tag ) )
-			bibtex_cleantoken( tok );
-	}
-	newstr_empty( s );
-	for ( i=0; i<tokens.n; ++i ) {
-		tok = list_get( &tokens, i );
-		if ( i>0 ) newstr_addchar( s, ' ' );
-		newstr_newstrcat( s, tok );
-	}
-out:
-	list_free( &tokens );
-	return status;
-}
-
-static int
-bibtexin_cleanref( fields *bibin, param *p )
-{
-	int i, n, status;
-	newstr *t, *d;
-	n = fields_num( bibin );
-	for ( i=0; i<n; ++i ) {
-		t = fields_tag( bibin, i, FIELDS_STRP_NOUSE );
-		d = fields_value( bibin, i, FIELDS_STRP_NOUSE );
-		status = bibtex_cleandata( t, d, bibin, p );
-		if ( status!=BIBL_OK ) return status;
-	}
-	return BIBL_OK;
-}
-
-static long
-bibtexin_findref( bibl *bin, char *citekey )
-{
-	int n;
-	long i;
-	for ( i=0; i<bin->nrefs; ++i ) {
-		n = fields_find( bin->ref[i], "refnum", -1 );
-		if ( n==-1 ) continue;
-		if ( !strcmp( bin->ref[i]->data[n].data, citekey ) ) return i;
-	}
-	return -1;
-}
-
-static void
-bibtexin_nocrossref( bibl *bin, long i, int n, param *p )
-{
-	int n1 = fields_find( bin->ref[i], "REFNUM", -1 );
-	if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-	fprintf( stderr, "Cannot find cross-reference '%s'",
-			bin->ref[i]->data[n].data );
-	if ( n1!=-1 ) fprintf( stderr, " for reference '%s'\n",
-			bin->ref[i]->data[n1].data );
-	fprintf( stderr, "\n" );
-}
-
-static int
-bibtexin_crossref_oneref( fields *bibref, fields *bibcross )
-{
-	int j, n, nl, ntype, fstatus, status = BIBL_OK;
-	char *type, *nt, *nv;
-
-	ntype = fields_find( bibref, "INTERNAL_TYPE", -1 );
-	type = ( char * ) fields_value( bibref, ntype, FIELDS_CHRP_NOUSE );
-
-	n = fields_num( bibcross );
-	for ( j=0; j<n; ++j ) {
-		nt = ( char * ) fields_tag( bibcross, j, FIELDS_CHRP_NOUSE );
-		if ( !strcasecmp( nt, "INTERNAL_TYPE" ) ) continue;
-		if ( !strcasecmp( nt, "REFNUM" ) ) continue;
-		if ( !strcasecmp( nt, "TITLE" ) ) {
-			if ( !strcasecmp( type, "Inproceedings" ) ||
-			     !strcasecmp( type, "Incollection" ) )
-				nt = "booktitle";
-		}
-		nv = ( char * ) fields_value( bibcross, j, FIELDS_CHRP_NOUSE );
-		nl = fields_level( bibcross, j ) + 1;
-		fstatus = fields_add( bibref, nt, nv, nl );
-		if ( fstatus!=FIELDS_OK ) {
-			status = BIBL_ERR_MEMERR;
-			goto out;
-		}
-	}
-out:
-	return status;
-}
-
-static int
-bibtexin_crossref( bibl *bin, param *p )
-{
-	int i, n, ncross, status = BIBL_OK;
-	fields *bibref, *bibcross;
-
-	for ( i=0; i<bin->nrefs; ++i ) {
-		bibref = bin->ref[i];
-		n = fields_find( bibref, "CROSSREF", -1 );
-		if ( n==-1 ) continue;
-		fields_setused( bibref, n );
-		ncross = bibtexin_findref( bin, (char*) fields_value( bibref, n, FIELDS_CHRP ) );
-		if ( ncross==-1 ) {
-			bibtexin_nocrossref( bin, i, n, p );
-			continue;
-		}
-		bibcross = bin->ref[ncross];
-		status = bibtexin_crossref_oneref( bibref, bibcross );
-		if ( status!=BIBL_OK ) goto out;
-	}
-out:
-	return status;
-}
-
-int
-bibtexin_cleanf( bibl *bin, param *p )
-{
-	int status = BIBL_OK;
-	long i;
-
-        for ( i=0; i<bin->nrefs; ++i )
-		status = bibtexin_cleanref( bin->ref[i], p );
-	bibtexin_crossref( bin, p );
-	return status;
-}
-
-/*****************************************************
- PUBLIC: int bibtexin_typef()
-*****************************************************/
-
-int
-bibtexin_typef( fields *bibin, char *filename, int nrefs, param *p,
-		variants *all, int nall )
-{
-	char *refnum = "";
-	int reftype, n, nrefnum;
-	n = fields_find( bibin, "INTERNAL_TYPE", 0 );
-	nrefnum = fields_find( bibin, "REFNUM", 0 );
-	if ( nrefnum!=-1 ) refnum = (bibin->data[nrefnum]).data;
-	if ( n!=-1 )
-		/* figure out type */
-		reftype = get_reftype( (bibin->data[n]).data, nrefs,
-			p->progname, all, nall, refnum );
-	else
-		/* no type info, go for default */
-		reftype = get_reftype( "", nrefs, p->progname, all, nall, refnum );
-	return reftype;
-}
-
-/*****************************************************
- PUBLIC: int bibtexin_convertf(), returns BIBL_OK or BIBL_ERR_MEMERR
-*****************************************************/
-
-static int
-bibtex_matches_list( fields *info, char *tag, char *suffix, newstr *data, int level,
-		list *names, int *match )
-{
-	int i, fstatus, status = BIBL_OK;
-	newstr newtag;
-
-	*match = 0;
-	if ( names->n==0 ) return status;
-
-	newstr_init( &newtag );
-
-	for ( i=0; i<names->n; ++i ) {
-		if ( strcmp( data->data, list_getc( names, i ) ) ) continue;
-		newstr_initstr( &newtag, tag );
-		newstr_strcat( &newtag, suffix );
-		fstatus = fields_add( info, newtag.data, data->data, level );
-		if ( fstatus!=FIELDS_OK ) {
-			status = BIBL_ERR_MEMERR;
-			goto out;
-		}
-		*match = 1;
-		goto out;
-	}
-
-out:
-	newstr_free( &newtag );
-	return status;
-}
-
-/*
- * bibtex_names( info, newtag, field, level);
- *
- * split names in author list separated by and's (use '|' character)
- * and add names
- *
- * returns BIBL_OK on success, BIBL_ERR_MEMERR on memory error
- */
-static int
-bibtex_names( fields *info, char *tag, newstr *data, int level, list *asis,
-	list *corps )
-{
-	int begin, end, ok, n, etal, i, status, match;
-	list tokens;
-
-	/* If we match the asis or corps list add and bail. */
-	status = bibtex_matches_list( info, tag, ":ASIS", data, level, asis, &match );
-	if ( match==1 || status!=BIBL_OK ) return status;
-	status = bibtex_matches_list( info, tag, ":CORP", data, level, corps, &match );
-	if ( match==1 || status!=BIBL_OK ) return status;
-
-	list_init( &tokens );
-
-	bibtex_split( &tokens, data );
-	for ( i=0; i<tokens.n; ++i )
-		bibtex_cleantoken( list_get( &tokens, i ) );
-
-	etal = name_findetal( &tokens );
-
-	begin = 0;
-	n = tokens.n - etal;
-	while ( begin < n ) {
-
-		end = begin + 1;
-
-		while ( end < n && strcasecmp( list_getc( &tokens, end ), "and" ) )
-			end++;
-
-		if ( end - begin == 1 ) {
-			ok = name_addsingleelement( info, tag, list_getc( &tokens, begin ), level, 0 );
-			if ( !ok ) { status = BIBL_ERR_MEMERR; goto out; }
-		} else {
-			ok = name_addmultielement( info, tag, &tokens, begin, end, level );
-			if ( !ok ) { status = BIBL_ERR_MEMERR; goto out; }
-		}
-
-		begin = end + 1;
-
-		/* Handle repeated 'and' errors: authors="G. F. Author and and B. K. Author" */
-		while ( begin < n && !strcasecmp( list_getc( &tokens, begin ), "and" ) )
-			begin++;
-	}
-
-	if ( etal ) {
-		ok = name_addsingleelement( info, tag, "et al.", level, 0 );
-		if ( !ok ) status = BIBL_ERR_MEMERR;
-	}
-
-out:
-	list_free( &tokens );
-	return status;
-}
-
-/* is_utf8_emdash()
- *
- * Internally pages="A---B" will convert --- to a UTF8
- * emdash = 0xE2 (-30) 0x80 (-128) 0x94 (-108)
- */
-static int
-is_utf8_emdash( char *p )
-{
-	static char emdash[3] = { -30, -128, -108 };
-	if ( strncmp( p, emdash, 3 ) ) return 0;
-	return 1;
-}
-/* is_utf8_endash()
- *
- * Internally pages="A--B" will convert -- to a UTF8
- * endash = 0xE2 (-30) 0x80 (-128) 0x93 (-109)
- */
-static int
-is_utf8_endash( char *p )
-{
-	static char endash[3] = { -30, -128, -109 };
-	if ( strncmp( p, endash, 3 ) ) return 0;
-	return 1;
-}
-
-static int
-process_pages( fields *info, newstr *s, int level )
-{
-	int fstatus, status = BIBL_OK;
-	newstr page;
-	char *p;
-
-	newstr_findreplace( s, " ", "" );
-	if ( s->len==0 ) return 1;
-
-	newstr_init( &page );
-	p = skip_ws( s->data );
-	while ( *p && !is_ws(*p) && *p!='-' && *p!='\r' && *p!='\n' && *p!=-30 )
-		newstr_addchar( &page, *p++ );
-	if ( page.len>0 ) {
-		fstatus = fields_add( info, "PAGESTART", page.data, level );
-		if ( fstatus!=FIELDS_OK ) {
-			status = BIBL_ERR_MEMERR;
-			goto out;
-		}
-	}
-
-	while ( *p && (is_ws(*p) || *p=='-' ) ) p++;
-	if ( *p && is_utf8_emdash( p ) ) p+=3;
-	if ( *p && is_utf8_endash( p ) ) p+=3;
-
-	newstr_empty( &page );
-	while ( *p && !is_ws(*p) && *p!='-' && *p!='\r' && *p!='\n' )
-		newstr_addchar( &page, *p++ );
-	if ( page.len>0 ) {
-		fstatus = fields_add( info, "PAGEEND", page.data, level );
-		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR; 
-	}
-
-out:
-	newstr_free( &page );
-	return status;
-}
-
-static int
-process_urlcore( fields *info, newstr *d, int level, char *default_tag )
-{
-	char *p = d->data;
-	int fstatus;
-	if ( !strncasecmp( p, "\\urllink", 8 ) )
-		fstatus = fields_add( info, "URL", p+8, level );
-	else if ( !strncasecmp( p, "\\url", 4 ) )
-		fstatus = fields_add( info, "URL", p+4, level );
-	else if ( !strncasecmp( p, "arXiv:", 6 ) )
-		fstatus = fields_add( info, "ARXIV", p+6, level );
-	else if ( !strncasecmp( p, "http://arxiv.org/abs/", 21 ) )
-		fstatus = fields_add( info, "ARXIV", p+21, level );
-	else if ( !strncasecmp( p, "http:", 5 ) )
-		fstatus = fields_add( info, "URL", p, level );
-	else
-		fstatus = fields_add( info, default_tag, p, level );
-	if ( fstatus==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
-
-static int
-process_url( fields *info, newstr *d, int level )
-{
-	return process_urlcore( info, d, level, "URL" );
-}
-
-/* Split keywords="" with semicolons.
- * Commas are also frequently used, but will break
- * entries like:
- *       keywords="Microscopy, Confocal"
- * Returns BIBL_OK or BIBL_ERR_MEMERR
- */
-static int
-process_keywords( fields *info, newstr *d, int level )
-{
-	int fstatus, status = BIBL_OK;
-	newstr keyword;
-	char *p;
-
-	if ( !d || d->len==0 ) return BIBL_OK;
-
-	p = d->data;
-	newstr_init( &keyword );
-
-	while ( *p ) {
-		p = newstr_cpytodelim( &keyword, skip_ws( p ), ";", 1 );
-		newstr_trimendingws( &keyword );
-		if ( newstr_memerr( &keyword ) ) {
-			status = BIBL_ERR_MEMERR;
-			goto out;
-		}
-		if ( keyword.len ) {
-			fstatus = fields_add( info, "KEYWORD", keyword.data, level );
-			if ( fstatus!=FIELDS_OK ) {
-				status = BIBL_ERR_MEMERR;
-				goto out;
-			}
-		}
-	}
-out:
-	newstr_free( &keyword );
-	return status;
-}
-
-/* proces_howpublished()
- *
- *    howpublished={},
- *
- * Normally indicates the manner in which something was
- * published in lieu of a formal publisher, so typically
- * 'howpublished' and 'publisher' will never be in the
- * same reference.
- *
- * Occassionally, people put Diploma thesis information
- * into the field, so check that first.
- *
- * Returns BIBL_OK or BIBL_ERR_MEMERR
- */
-static int
-process_howpublished( fields *info, newstr *d, int level )
-{
-	int fstatus, status = BIBL_OK;
-	char *p = d->data;
-	if ( !strncasecmp( p, "Diplom", 6 ) ) {
-		fstatus = fields_replace_or_add( info, "GENRE", "Diploma thesis", level );
-		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
-	} else if ( !strncasecmp( p, "Habilitation", 13 ) ) {
-		fstatus = fields_replace_or_add( info, "GENRE", "Habilitation thesis", level );
-		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
-	} else if ( !strncasecmp( d->data, "http:", 5 ) ) {
-		status = process_url( info, d, level );
-	} else if ( !strncasecmp( d->data, "arXiv:", 6 ) ) {
-		status = process_url( info, d, level );
-	} else {
-		fstatus = fields_add( info, "PUBLISHER", p, level );
-		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
-	}
-	return status;
-}
-
-/*
- * sentelink = {file://localhost/full/path/to/file.pdf,Sente,PDF}
- */
-static int
-process_sente( fields *info, newstr *d, int level )
-{
-	int fstatus, status = BIBL_OK;
-	newstr link;
-
-	newstr_init( &link );
-	newstr_cpytodelim( &link, skip_ws( d->data ), ",", 0 );
-	newstr_trimendingws( &link );
-	if ( newstr_memerr( &link ) ) status = BIBL_ERR_MEMERR;
-
-	if ( status==BIBL_OK && link.len ) {
-		fstatus = fields_add( info, "FILEATTACH", link.data, level );
-		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
-	}
-
-	newstr_free( &link );
-	return status;
-}
-
-/*
- * BibTeX uses 'organization' in lieu of publisher if that field is missing.
- * Otherwise output as
- * <name type="corporate">
- *    <namePart>The organization</namePart>
- *    <role>
- *       <roleTerm authority="marcrelator" type="text">organizer of meeting</roleTerm>
- *    </role>
- * </name>
- */
-static int
-process_organization( fields *bibin, fields *info, newstr *d, int level )
-{
-	int n, fstatus;
-	n = fields_find( bibin, "publisher", LEVEL_ANY );
-	if ( n==-1 )
-		fstatus = fields_add( info, "PUBLISHER", d->data, level );
-	else
-		fstatus = fields_add( info, "ORGANIZER:CORP", d->data, level );
-	if ( fstatus==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
-
-static int
-count_colons( char *p )
-{
-	int n = 0;
-	while ( *p ) {
-		if ( *p==':' ) n++;
-		p++;
-	}
-	return n;
-}
-
-static int
-first_colon( char *p )
-{
-	int n = 0;
-	while ( p[n] && p[n]!=':' ) n++;
-	return n;
-}
-
-static int
-last_colon( char *p )
-{
-	int n = strlen( p ) - 1;
-	while ( n>0 && p[n]!=':' ) n--;
-	return n;
-}
-
-/*
- * file={Description:/full/path/to/file.pdf:PDF}
- */
-static int
-process_file( fields *info, newstr *d, int level )
-{
-	int fstatus, status = BIBL_OK;
-	char *p = d->data;
-	int i, n, n1, n2;
-	newstr link;
-
-	n = count_colons( p );
-	if ( n > 1 ) {
-		/* A DOS file can contain a colon ":C:/....pdf:PDF" */
-		/* Extract after 1st and up to last colons */
-		n1 = first_colon( p ) + 1;
-		n2 = last_colon( p );
-		newstr_init( &link );
-		for ( i=n1; i<n2; ++i ) {
-			newstr_addchar( &link, p[i] );
-		}
-		newstr_trimstartingws( &link );
-		newstr_trimendingws( &link );
-		if ( newstr_memerr( &link ) ) {
-			status = BIBL_ERR_MEMERR;
-			goto out;
-		}
-		if ( link.len ) {
-			fstatus = fields_add( info, "FILEATTACH", link.data, level );
-			if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
-		}
-out:
-		newstr_free( &link );
-	} else {
-		/* This field isn't formatted properly, so just copy directly */
-		fstatus = fields_add( info, "FILEATTACH", p, level );
-		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
-	}
-	return status;
-}
-
-static int
-process_note( fields *info, newstr *d, int level )
-{
-	int fstatus, status = BIBL_OK;
-	if ( !strncasecmp( d->data, "http:", 5 ) ||
-	     !strncasecmp( d->data, "arXiv:", 6 ) ) {
-		status = process_url( info, d, level );
-	} else {
-		fstatus = fields_add( info, "NOTES", d->data, level );
-		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-	}
-	return status;
-}
-
-static void
-bibtexin_notag( param *p, char *tag )
-{
-	if ( p->verbose && strcmp( tag, "INTERNAL_TYPE" ) ) {
-		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-		fprintf( stderr, "Cannot find tag '%s'\n", tag );
-	}
-}
-
-/* bibtexin_titleinbook_isbooktitle()
- *
- * Normally, the title field of inbook refers to the book.  The
- * section in a @inbook reference is untitled.  If it's titled,
- * the @incollection should be used.  For example, in:
- *
- * @inbook{
- *    title="xxx"
- * }
- *
- * the booktitle is "xxx".
- *
- * However, @inbook is frequently abused (and treated like
- * @incollection) so that title and booktitle are present
- * and title is now 'supposed' to refer to the section.  For example:
- *
- * @inbook{
- *     title="yyy",
- *     booktitle="xxx"
- * }
- *
- * Therefore report whether or not booktitle is present as well
- * as title in @inbook references.  If not, then make 'title'
- * correspond to the title of the book, not the section.
- *
- */
-static int
-bibtexin_titleinbook_isbooktitle( char *intag, fields *bibin )
-{
-	int n;
-
-	/* ...look only at 'title="xxx"' elements */
-	if ( strcasecmp( intag, "TITLE" ) ) return 0;
-
-	/* ...look only at '@inbook' references */
-	n = fields_find( bibin, "INTERNAL_TYPE", -1 );
-	if ( n==-1 ) return 0;
-	if ( strcasecmp( fields_value( bibin, n, FIELDS_CHRP ), "INBOOK" ) ) return 0;
-
-	/* ...look to see if 'booktitle="yyy"' exists */
-	n = fields_find( bibin, "BOOKTITLE", -1 );
-	if ( n==-1 ) return 0;
-	else return 1;
-}
-static int
-bibtexin_title_process( fields *info, char *outtag, fields *bibin, newstr *t, newstr *d, int level, int nosplittitle )
-{
-	char *intag = t->data;
-	char *indata = d->data;
-	int ok;
-	if ( bibtexin_titleinbook_isbooktitle( intag, bibin ) ) level=LEVEL_MAIN;
-	ok = title_process( info, outtag, indata, level, nosplittitle );
-	if ( ok ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
-
-static int
-bibtex_simple( fields *info, char *outtag, newstr *d, int level )
-{
-	int fstatus;
-	fstatus = fields_add( info, outtag, d->data, level );
-	if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-	return BIBL_OK;
-}
-
-int
-bibtexin_convertf( fields *bibin, fields *info, int reftype, param *p,
-		variants *all, int nall )
-{
-	int process, level, i, n, nfields, status;
-	newstr *t, *d;
-	char *outtag;
-
-	nfields = fields_num( bibin );
-	for ( i=0; i<nfields; ++i ) {
-
-		if ( fields_used( bibin, i ) ) continue; /* e.g. successful crossref */
-		if ( fields_nodata( bibin, i ) ) continue;
-
-		t = fields_tag( bibin, i, FIELDS_STRP );
-		if ( t->len == 0 ) continue; /* Don't consider with null tags */
-		n = process_findoldtag( t->data, reftype, all, nall );
-		if ( n==-1 ) {
-			bibtexin_notag( p, t->data );
-			continue;
-		}
-
-		d = fields_value( bibin, i, FIELDS_STRP );
-
-		process = ((all[reftype]).tags[n]).processingtype;
-		level   = ((all[reftype]).tags[n]).level;
-		outtag  = ((all[reftype]).tags[n]).newstr;
-
-		switch( process ) {
-
-		case SIMPLE:
-			status = bibtex_simple( info, outtag, d, level );
-			break;
-
-		case TITLE:
-			status = bibtexin_title_process( info, "TITLE", bibin, t, d, level, p->nosplittitle );
-			break;
-
-		case PERSON:
-			status = bibtex_names( info, outtag, d, level, &(p->asis), &(p->corps) );
-			break;
-
-		case PAGES:
-			status = process_pages( info, d, level );
-			break;
-
-		case KEYWORD:
-			status = process_keywords( info, d, level );
-			break;
-
-		case HOWPUBLISHED:
-			status = process_howpublished( info, d, level );
-			break;
-
-		case LINKEDFILE:
-			status = process_file( info, d, level );
-			break;
-
-		case BT_NOTE:
-			status = process_note( info, d, level );
-			break;
-
-		case BT_SENTE:
-			status = process_sente( info, d, level );
-			break;
-
-		case BT_URL:
-			status = process_url( info, d, level );
-			break;
-
-		case BT_ORG:
-			status = process_organization( bibin, info, d, level );
-			break;
-
-		default:
-			status = BIBL_OK;
-			break;
-		}
-
-		if ( status!=BIBL_OK ) return status;
-	}
-	return status;
-}
-
+ * Copyright (c) Chris Putnam 2003-2017
+ *
+ * Program and source code released under the GPL version 2
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "is_ws.h"
+#include "str.h"
+#include "utf8.h"
+#include "str_conv.h"
+#include "fields.h"
+#include "slist.h"
+#include "name.h"
+#include "title.h"
+#include "url.h"
+#include "reftypes.h"
+#include "bibformats.h"
+#include "generic.h"
+
+static slist find    = { 0, 0, 0, NULL };
+static slist replace = { 0, 0, 0, NULL };
+
+extern variants bibtex_all[];
+extern int bibtex_nall;
+
+/*****************************************************
+ 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 );
+
+void
+bibtexin_initparams( param *p, 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;
+
+	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;
+
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
+
+	if ( !progname ) p->progname = NULL;
+	else p->progname = strdup( progname );
+}
+
+/*****************************************************
+ PUBLIC: int bibtexin_readf()
+*****************************************************/
+
+/*
+ * readf can "read too far", so we store this information in line, thus
+ * the next new text is in line, either from having read too far or
+ * from the next chunk obtained via str_fget()
+ *
+ * return 1 on success, 0 on error/end-of-file
+ *
+ */
+static int
+readmore( FILE *fp, char *buf, int bufsize, int *bufpos, str *line )
+{
+	if ( line->len ) return 1;
+	else return str_fget( fp, buf, bufsize, bufpos, line );
+}
+
+/*
+ * readf()
+ *
+ * returns zero if cannot get reference and hit end of-file
+ * returns 1 if last reference in file, 2 if reference within file
+ */
+static int
+bibtexin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
+{
+	int haveref = 0;
+	char *p;
+	*fcharset = CHARSET_UNKNOWN;
+	while ( haveref!=2 && readmore( fp, buf, bufsize, bufpos, line ) ) {
+		if ( line->len == 0 ) continue; /* blank line */
+		p = &(line->data[0]);
+		/* Recognize UTF8 BOM */
+		if ( line->len > 2 && 
+				(unsigned char)(p[0])==0xEF &&
+				(unsigned char)(p[1])==0xBB &&
+				(unsigned char)(p[2])==0xBF ) {
+			*fcharset = CHARSET_UNICODE;
+			p += 3;
+		}
+		p = skip_ws( p );
+		if ( *p == '%' ) { /* commented out line */
+			str_empty( line );
+			continue;
+		}
+		if ( *p == '@' ) haveref++;
+		if ( haveref && haveref<2 ) {
+			str_strcatc( reference, p );
+			str_addchar( reference, '\n' );
+			str_empty( line );
+		} else if ( !haveref ) str_empty( line );
+	
+	}
+	return haveref;
+}
+
+/*****************************************************
+ PUBLIC: int bibtexin_processf()
+*****************************************************/
+
+static char*
+process_bibtextype( char *p, str *type )
+{
+	str tmp;
+	str_init( &tmp );
+
+	if ( *p=='@' ) p++;
+	p = str_cpytodelim( &tmp, p, "{( \t\r\n", 0 );
+	p = skip_ws( p );
+	if ( *p=='{' || *p=='(' ) p++;
+	p = skip_ws( p );
+
+	if ( str_has_value( &tmp ) ) str_strcpy( type, &tmp );
+	else str_empty( type );
+
+	str_free( &tmp );
+	return p;
+}
+
+static char*
+process_bibtexid( char *p, str *id )
+{
+	char *start_p = p;
+	str tmp;
+
+	str_init( &tmp );
+	p = str_cpytodelim( &tmp, p, ",", 1 );
+
+	if ( str_has_value( &tmp ) ) {
+		if ( strchr( tmp.data, '=' ) ) {
+			/* Endnote writes bibtex files w/o fields, try to
+			 * distinguish via presence of an equal sign.... if
+			 * it's there, assume that it's a tag/data pair instead
+			 * and roll back.
+			 */
+			p = start_p;
+			str_empty( id );
+		} else {
+			str_strcpy( id, &tmp );
+		}
+	} else {
+		str_empty( id );
+	}
+
+	str_free( &tmp );
+	return skip_ws( p );
+}
+
+static char *
+bibtex_tag( char *p, str *tag )
+{
+	p = str_cpytodelim( tag, skip_ws( 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 )
+{
+	unsigned int nbracket = 0, nquotes = 0;
+	char *startp = p;
+	str tok, *t;
+
+	str_init( &tok );
+	while ( p && *p ) {
+		if ( !nquotes && !nbracket ) {
+			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 );
+			}
+		} 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; }
+			}
+			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 ( !is_ws( *p ) || nquotes || nbracket ) {
+			if ( !is_ws( *p ) ) str_addchar( &tok, *p );
+			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, ' ' );
+					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 );
+			}
+		}
+		p++;
+	}
+out:
+	if ( nbracket!=0 ) {
+		fprintf( stderr, "%s: Mismatch in number of brackets in reference %ld.\n", pm->progname, nref );
+	}
+	if ( nquotes!=0 ) {
+		fprintf( stderr, "%s: Mismatch in number of quotes in reference %ld.\n", pm->progname, nref );
+	}
+	if ( str_has_value( &tok ) ) {
+		if ( str_memerr( &tok ) ) { p = NULL; goto out; }
+		t = slist_add( tokens, &tok );
+		if ( !t ) p = NULL;
+	}
+out0:
+	str_free( &tok );
+	return p;
+}
+
+/* replace_strings()
+ *
+ * do string replacement -- only if unprotected by quotation marks or curly brackets
+ */
+static void
+replace_strings( slist *tokens, fields *bibin, param *pm )
+{
+	int i, n, ok;
+	char *q;
+	str *s;
+	i = 0;
+	while ( i < tokens->n ) {
+		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++;
+				}
+				if ( !ok ) {
+					fprintf( stderr, "%s: Warning: Non-numeric "
+					   "BibTeX elements should be in quotations or "
+					   "curly brackets in reference.\n", pm->progname );
+				}
+			}
+		}
+		i++;
+	}
+}
+
+static int
+string_concatenate( slist *tokens, fields *bibin, long nref, param *pm )
+{
+	int i, status;
+	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;
+			status = slist_remove( tokens, i );
+			if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
+		} else i++;
+	}
+	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 )
+{
+	int i, status;
+	slist tokens;
+	str *s;
+
+	str_empty( data );
+
+	p = bibtex_tag( p, tag );
+	if ( p==NULL || str_is_empty( tag ) ) return p;
+
+	slist_init( &tokens );
+
+	if ( *p=='=' ) {
+		p = bibtex_data( p+1, bibin, &tokens, nref, pm );
+		if ( p==NULL ) goto out;
+	}
+
+	replace_strings( &tokens, bibin, pm );
+
+	status = string_concatenate( &tokens, bibin, nref, pm );
+	if ( status!=BIBL_OK ) {
+		p = NULL;
+		goto out;
+	}
+
+	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 ) );
+	}
+out:
+	slist_free( &tokens );
+	return p;
+}
+
+/* process_cite()
+ *
+ */
+static int
+process_cite( fields *bibin, char *p, char *filename, long nref, param *pm )
+{
+	int fstatus, status = BIBL_OK;
+	str tag, data;
+
+	strs_init( &tag, &data, NULL );
+
+	p = process_bibtextype( p, &data );
+	if ( str_has_value( &data ) ) {
+		fstatus = fields_add( bibin, "INTERNAL_TYPE", str_cstr( &data ), 0 );
+		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
+	}
+
+	p = process_bibtexid( p, &data );
+	if ( str_has_value( &data ) ) {
+		fstatus = fields_add( bibin, "REFNUM", str_cstr( &data ), 0 );
+		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
+	}
+
+	while ( *p ) {
+		p = process_bibtexline( p, &tag, &data, 1, bibin, nref, pm );
+		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 );
+	}
+out:
+	strs_free( &tag, &data, NULL );
+	return status;
+}
+
+/* process_string()
+ *
+ * Handle lines like:
+ *
+ * '@STRING{TL = {Tetrahedron Lett.}}'
+ *
+ * p should point to just after '@STRING'
+ *
+ * In BibTeX, if a string is defined several times, the last one is kept.
+ *
+ */
+static int
+process_string( char *p, long nref, param *pm )
+{
+	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 );
+	if ( p==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
+	if ( str_has_value( &s2 ) ) {
+		str_findreplace( &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; }
+		} else {
+			if ( str_has_value( &s2 ) ) t = slist_set( &replace, n, &s2 );
+			else t = slist_setc( &replace, n, "" );
+			if ( t==NULL ) { status = BIBL_ERR_MEMERR; goto out; }
+		}
+	}
+out:
+	strs_free( &s1, &s2, NULL );
+	return status;
+}
+
+/* bibtexin_processf()
+ *
+ * Handle '@STRING', '@reftype', and ignore '@COMMENT'
+ */
+static int
+bibtexin_processf( fields *bibin, char *data, char *filename, long nref, param *p )
+{
+	if ( !strncasecmp( data, "@STRING", 7 ) ) {
+		process_string( data+7, nref, p );
+		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 );
+		return 1;
+	}
+}
+
+/*****************************************************
+ PUBLIC: void bibtexin_cleanf()
+*****************************************************/
+
+static int
+bibtex_protected( str *data )
+{
+	if ( data->data[0]=='{' && data->data[data->len-1]=='}' ) return 1;
+	if ( data->data[0]=='\"' && data->data[data->len-1]=='\"' ) return 1;
+	return 0;
+}
+
+static int
+bibtex_split( slist *tokens, str *s )
+{
+	int i, n = s->len, nbrackets = 0, status = BIBL_OK;
+	str tok, *t;
+
+	str_init( &tok );
+
+	for ( i=0; i<n; ++i ) {
+		if ( s->data[i]=='{' && ( i==0 || s->data[i-1]!='\\' ) ) {
+			nbrackets++;
+			str_addchar( &tok, '{' );
+		} else if ( s->data[i]=='}' && ( i==0 || s->data[i-1]!='\\' ) ) {
+			nbrackets--;
+			str_addchar( &tok, '}' );
+		} else if ( !is_ws( s->data[i] ) || nbrackets ) {
+			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;
+				}
+			}
+			str_empty( &tok );
+		}
+	}
+	if ( str_has_value( &tok ) ) {
+		t = slist_add( tokens, &tok );
+		if ( !t ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+	}
+
+	for ( i=0; i<tokens->n; ++i ) {
+		str_trimstartingws( slist_str( tokens, i ) );
+		str_trimendingws( slist_str( tokens, i ) );
+	}
+out:
+	str_free( &tok );
+	return status;
+}
+
+static int
+bibtex_addtitleurl( fields *info, str *in )
+{
+	int fstatus, status = BIBL_OK;
+	str s;
+	char *p;
+
+	str_init( &s );
+
+	/* ...skip past "\href{" and copy to "}" */
+	p = str_cpytodelim( &s, in->data + 6, "}", 1 );
+	if ( str_memerr( &s ) ) { status = BIBL_ERR_MEMERR; goto out; }
+
+	/* ...add to URL */
+	fstatus = fields_add( info, "URL", s.data, 0 );
+	if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
+
+	/* ...return deleted fragment to str in */
+	p = str_cpytodelim( &s, p, "", 0 );
+	if ( str_memerr( &s ) ) { status = BIBL_ERR_MEMERR; goto out; }
+	str_swapstrings( &s, in );
+
+out:
+	str_free( &s );
+	return status;
+}
+
+static int
+is_url_tag( str *tag )
+{
+	if ( str_has_value( tag ) ) {
+		if ( !strcasecmp( str_cstr( tag ), "url" ) ) return 1;
+	}
+	return 0;
+}
+
+static int
+is_name_tag( str *tag )
+{
+	if ( str_has_value( tag ) ) {
+		if ( !strcasecmp( str_cstr( tag ), "author" ) ) return 1;
+		if ( !strcasecmp( str_cstr( tag ), "editor" ) ) return 1;
+	}
+	return 0;
+}
+
+static void
+bibtex_process_tilde( str *s )
+{
+	char *p, *q;
+	int n = 0;
+
+	p = q = s->data;
+	if ( !p ) return;
+	while ( *p ) {
+		if ( *p=='~' ) {
+			*q = ' ';
+		} else if ( *p=='\\' && *(p+1)=='~' ) {
+			n++;
+			p++;
+			*q = '~';
+		} else {
+			*q = *p;
+		}
+		p++;
+		q++;
+	}
+	*q = '\0';
+	s->len -= n;
+}
+
+static void
+bibtex_process_bracket( str *s )
+{
+	char *p, *q;
+	int n = 0;
+
+	p = q = s->data;
+	if ( !p ) return;
+	while ( *p ) {
+		if ( *p=='\\' && ( *(p+1)=='{' || *(p+1)=='}' ) ) {
+			n++;
+			p++;
+			*q = *p;
+			q++;
+		} else if ( *p=='{' || *p=='}' ) {
+			n++;
+		} else {
+			*q = *p;
+			q++;
+		}
+		p++;
+	}
+	*q = '\0';
+	s->len -= n;
+}
+
+static void
+bibtex_cleantoken( str *s )
+{
+	/* 'textcomp' annotations */
+	str_findreplace( s, "\\textit", "" );
+	str_findreplace( s, "\\textbf", "" );
+	str_findreplace( s, "\\textsl", "" );
+	str_findreplace( s, "\\textsc", "" );
+	str_findreplace( s, "\\textsf", "" );
+	str_findreplace( s, "\\texttt", "" );
+	str_findreplace( s, "\\textsubscript", "" );
+	str_findreplace( s, "\\textsuperscript", "" );
+	str_findreplace( s, "\\emph", "" );
+	str_findreplace( s, "\\url", "" );
+	str_findreplace( s, "\\mbox", "" );
+
+	/* Other text annotations */
+	str_findreplace( s, "\\it ", "" );
+	str_findreplace( s, "\\em ", "" );
+
+	str_findreplace( s, "\\%", "%" );
+	str_findreplace( s, "\\$", "$" );
+	while ( str_findreplace( s, "  ", " " ) ) {}
+
+	/* 'textcomp' annotations that we don't want to substitute on output*/
+	str_findreplace( s, "\\textdollar", "$" );
+	str_findreplace( s, "\\textunderscore", "_" );
+
+	bibtex_process_bracket( s );
+	bibtex_process_tilde( s );
+
+}
+
+static int
+bibtex_cleandata( str *tag, str *s, fields *info, param *p )
+{
+	int i, status;
+	slist tokens;
+	str *tok;
+	if ( str_is_empty( s ) ) return BIBL_OK;
+	/* protect url from undergoing any parsing */
+	if ( is_url_tag( tag ) ) return BIBL_OK;
+	slist_init( &tokens );
+	status = bibtex_split( &tokens, s );
+	if ( status!=BIBL_OK ) goto out;
+	for ( i=0; i<tokens.n; ++i ) {
+		tok = slist_str( &tokens, i );
+		if ( bibtex_protected( tok ) ) {
+			if (!strncasecmp(tok->data,"\\href{", 6)) {
+				bibtex_addtitleurl( info, tok );
+			}
+		}
+		if ( p->latexin && !is_name_tag( tag ) && !is_url_tag( tag ) )
+			bibtex_cleantoken( tok );
+	}
+	str_empty( s );
+	for ( i=0; i<tokens.n; ++i ) {
+		tok = slist_str( &tokens, i );
+		if ( i>0 ) str_addchar( s, ' ' );
+		str_strcat( s, tok );
+	}
+out:
+	slist_free( &tokens );
+	return status;
+}
+
+static int
+bibtexin_cleanref( fields *bibin, param *p )
+{
+	int i, n, status;
+	str *t, *d;
+	n = fields_num( bibin );
+	for ( i=0; i<n; ++i ) {
+		t = fields_tag( bibin, i, FIELDS_STRP_NOUSE );
+		d = fields_value( bibin, i, FIELDS_STRP_NOUSE );
+		status = bibtex_cleandata( t, d, bibin, p );
+		if ( status!=BIBL_OK ) return status;
+	}
+	return BIBL_OK;
+}
+
+static long
+bibtexin_findref( bibl *bin, char *citekey )
+{
+	int n;
+	long i;
+	for ( i=0; i<bin->nrefs; ++i ) {
+		n = fields_find( bin->ref[i], "refnum", LEVEL_ANY );
+		if ( n==-1 ) continue;
+		if ( !strcmp( bin->ref[i]->data[n].data, citekey ) ) return i;
+	}
+	return -1;
+}
+
+static void
+bibtexin_nocrossref( bibl *bin, long i, int n, param *p )
+{
+	int n1 = fields_find( bin->ref[i], "REFNUM", LEVEL_ANY );
+	if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
+	fprintf( stderr, "Cannot find cross-reference '%s'",
+			bin->ref[i]->data[n].data );
+	if ( n1!=-1 ) fprintf( stderr, " for reference '%s'\n",
+			bin->ref[i]->data[n1].data );
+	fprintf( stderr, "\n" );
+}
+
+static int
+bibtexin_crossref_oneref( fields *bibref, fields *bibcross )
+{
+	int j, n, nl, ntype, fstatus, status = BIBL_OK;
+	char *type, *nt, *nv;
+
+	ntype = fields_find( bibref, "INTERNAL_TYPE", LEVEL_ANY );
+	type = ( char * ) fields_value( bibref, ntype, FIELDS_CHRP_NOUSE );
+
+	n = fields_num( bibcross );
+	for ( j=0; j<n; ++j ) {
+		nt = ( char * ) fields_tag( bibcross, j, FIELDS_CHRP_NOUSE );
+		if ( !strcasecmp( nt, "INTERNAL_TYPE" ) ) continue;
+		if ( !strcasecmp( nt, "REFNUM" ) ) continue;
+		if ( !strcasecmp( nt, "TITLE" ) ) {
+			if ( !strcasecmp( type, "Inproceedings" ) ||
+			     !strcasecmp( type, "Incollection" ) )
+				nt = "booktitle";
+		}
+		nv = ( char * ) fields_value( bibcross, j, FIELDS_CHRP_NOUSE );
+		nl = fields_level( bibcross, j ) + 1;
+		fstatus = fields_add( bibref, nt, nv, nl );
+		if ( fstatus!=FIELDS_OK ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+	}
+out:
+	return status;
+}
+
+static int
+bibtexin_crossref( bibl *bin, param *p )
+{
+	int i, n, ncross, status = BIBL_OK;
+	fields *bibref, *bibcross;
+
+	for ( i=0; i<bin->nrefs; ++i ) {
+		bibref = bin->ref[i];
+		n = fields_find( bibref, "CROSSREF", LEVEL_ANY );
+		if ( n==-1 ) continue;
+		fields_setused( bibref, n );
+		ncross = bibtexin_findref( bin, (char*) fields_value( bibref, n, FIELDS_CHRP ) );
+		if ( ncross==-1 ) {
+			bibtexin_nocrossref( bin, i, n, p );
+			continue;
+		}
+		bibcross = bin->ref[ncross];
+		status = bibtexin_crossref_oneref( bibref, bibcross );
+		if ( status!=BIBL_OK ) goto out;
+	}
+out:
+	return status;
+}
+
+static int
+bibtexin_cleanf( bibl *bin, param *p )
+{
+	int status = BIBL_OK;
+	long i;
+
+        for ( i=0; i<bin->nrefs; ++i )
+		status = bibtexin_cleanref( bin->ref[i], p );
+	bibtexin_crossref( bin, p );
+	return status;
+}
+
+/*****************************************************
+ PUBLIC: int bibtexin_typef()
+*****************************************************/
+
+static int
+bibtexin_typef( fields *bibin, char *filename, int nrefs, param *p )
+{
+	int ntypename, nrefname, is_default;
+	char *refname = "", *typename = "";
+
+	ntypename = fields_find( bibin, "INTERNAL_TYPE", LEVEL_MAIN );
+	nrefname  = fields_find( bibin, "REFNUM",        LEVEL_MAIN );
+	if ( nrefname!=-1 )  refname  = fields_value( bibin, nrefname,  FIELDS_CHRP_NOUSE );
+	if ( ntypename!=-1 ) typename = fields_value( bibin, ntypename, FIELDS_CHRP_NOUSE );
+
+	return get_reftype( typename, nrefs, p->progname, p->all, p->nall, refname, &is_default, REFTYPE_CHATTY );
+}
+
+/*****************************************************
+ PUBLIC: int bibtexin_convertf(), returns BIBL_OK or BIBL_ERR_MEMERR
+*****************************************************/
+
+static int
+bibtex_matches_list( fields *bibout, char *tag, char *suffix, str *data, int level,
+		slist *names, int *match )
+{
+	int i, fstatus, status = BIBL_OK;
+	str newtag;
+
+	*match = 0;
+	if ( names->n==0 ) return status;
+
+	str_init( &newtag );
+
+	for ( i=0; i<names->n; ++i ) {
+		if ( strcmp( str_cstr( data ), slist_cstr( names, i ) ) ) continue;
+		str_initstrc( &newtag, tag );
+		str_strcatc( &newtag, suffix );
+		fstatus = fields_add( bibout, str_cstr( &newtag ), str_cstr( data ), level );
+		if ( fstatus!=FIELDS_OK ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+		*match = 1;
+		goto out;
+	}
+
+out:
+	str_free( &newtag );
+	return status;
+}
+
+/**** bibtexin_btorg ****/
+
+/*
+ * BibTeX uses 'organization' in lieu of publisher if that field is missing.
+ * Otherwise output as
+ * <name type="corporate">
+ *    <namePart>The organization</namePart>
+ *    <role>
+ *       <roleTerm authority="marcrelator" type="text">organizer of meeting</roleTerm>
+ *    </role>
+ * </name>
+ */
+
+static int
+bibtexin_btorg( fields *bibin, int m, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int n, fstatus;
+	n = fields_find( bibin, "publisher", LEVEL_ANY );
+	if ( n==-1 )
+		fstatus = fields_add( bibout, "PUBLISHER", str_cstr( invalue ), level );
+	else
+		fstatus = fields_add( bibout, "ORGANIZER:CORP", str_cstr( invalue ), level );
+	if ( fstatus==FIELDS_OK ) return BIBL_OK;
+	else return BIBL_ERR_MEMERR;
+}
+
+/**** bibtexin_btsente() ****/
+
+/*
+ * sentelink = {file://localhost/full/path/to/file.pdf,Sente,PDF}
+ *
+ * Sente is an academic reference manager for MacOSX and Apple iPad.
+ */
+
+static int
+bibtexin_btsente( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int fstatus, status = BIBL_OK;
+	str link;
+
+	str_init( &link );
+	str_cpytodelim( &link, skip_ws( invalue->data ), ",", 0 );
+	str_trimendingws( &link );
+	if ( str_memerr( &link ) ) status = BIBL_ERR_MEMERR;
+
+	if ( status==BIBL_OK && link.len ) {
+		fstatus = fields_add( bibout, "FILEATTACH", str_cstr( &link ), level );
+		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+	}
+
+	str_free( &link );
+	return status;
+}
+
+/**** bibtexin_linkedfile() ****/
+
+static int
+count_colons( char *p )
+{
+	int n = 0;
+	while ( *p ) {
+		if ( *p==':' ) n++;
+		p++;
+	}
+	return n;
+}
+
+static int
+first_colon( char *p )
+{
+	int n = 0;
+	while ( p[n] && p[n]!=':' ) n++;
+	return n;
+}
+
+static int
+last_colon( char *p )
+{
+	int n = strlen( p ) - 1;
+	while ( n>0 && p[n]!=':' ) n--;
+	return n;
+}
+
+/*
+ * file={Description:/full/path/to/file.pdf:PDF}
+ */
+static int
+bibtexin_linkedfile( fields *bibin, int m, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int fstatus, status = BIBL_OK;
+	char *p = invalue->data;
+	int i, n, n1, n2;
+	str link;
+
+	n = count_colons( p );
+	if ( n > 1 ) {
+		/* A DOS file can contain a colon ":C:/....pdf:PDF" */
+		/* Extract after 1st and up to last colons */
+		n1 = first_colon( p ) + 1;
+		n2 = last_colon( p );
+		str_init( &link );
+		for ( i=n1; i<n2; ++i ) {
+			str_addchar( &link, p[i] );
+		}
+		str_trimstartingws( &link );
+		str_trimendingws( &link );
+		if ( str_memerr( &link ) ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+		if ( link.len ) {
+			fstatus = fields_add( bibout, "FILEATTACH", link.data, level );
+			if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+		}
+out:
+		str_free( &link );
+	} else {
+		/* This field isn't formatted properly, so just copy directly */
+		fstatus = fields_add( bibout, "FILEATTACH", p, level );
+		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+	}
+	return status;
+
+}
+
+/**** bibtexin_howpublished() ****/
+
+/*    howpublished={},
+ *
+ * Normally indicates the manner in which something was
+ * published in lieu of a formal publisher, so typically
+ * 'howpublished' and 'publisher' will never be in the
+ * same reference.
+ *
+ * Occassionally, people put Diploma thesis information
+ * into the field, so check that first.
+ *
+ * Returns BIBL_OK or BIBL_ERR_MEMERR
+ */
+
+static int
+bibtexin_howpublished( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int fstatus, status = BIBL_OK;
+	if ( !strncasecmp( str_cstr( invalue ), "Diplom", 6 ) ) {
+		fstatus = fields_replace_or_add( bibout, "GENRE", "Diploma thesis", level );
+		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+	} else if ( !strncasecmp( str_cstr( invalue ), "Habilitation", 13 ) ) {
+		fstatus = fields_replace_or_add( bibout, "GENRE", "Habilitation thesis", level );
+		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+	} else if ( is_embedded_link( str_cstr( invalue ) ) ) {
+		status =  urls_split_and_add( str_cstr( invalue ), bibout, level );
+	} else {
+		fstatus = fields_add( bibout, "PUBLISHER", str_cstr( invalue ), level );
+		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+	}
+	return status;
+}
+
+/**** bibtexin_eprint() ****/
+
+/* Try to capture situations like
+ *
+ * eprint="1605.02026",
+ * archivePrefix="arXiv",
+ *
+ * or
+ *
+ * eprint="13211131",
+ * eprinttype="medline",
+ *
+ * If we don't know anything, concatenate archivePrefix:eprint
+ * and push into URL. (Could be wrong)
+ *
+ * If no info, just push eprint into URL. (Could be wrong)
+ */
+static int
+process_eprint_with_prefix( fields *bibout, char *prefix, str *value, int level )
+{
+	int fstatus, status = BIBL_OK;
+	str merge;
+
+	if ( !strcmp( prefix, "arXiv" ) ) {
+		fstatus = fields_add( bibout, "ARXIV", value->data, level );
+		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+	}
+
+	else if ( !strcmp( prefix, "jstor" ) ) {
+		fstatus = fields_add( bibout, "JSTOR", value->data, level );
+		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+	}
+
+	else if ( !strcmp( prefix, "medline" ) ) {
+		fstatus = fields_add( bibout, "MEDLINE", value->data, level );
+		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+	}
+
+	else if ( !strcmp( prefix, "pubmed" ) ) {
+		fstatus = fields_add( bibout, "PMID", value->data, level );
+		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+	}
+
+	/* ...if this is unknown prefix, merge prefix & eprint */
+	else {
+		str_init( &merge );
+		str_mergestrs( &merge, prefix, ":", value->data, NULL );
+		fstatus = fields_add( bibout, "URL", merge.data, level );
+		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+		str_free( &merge );
+	}
+
+	return status;
+}
+static int
+process_eprint_without_prefix( fields *bibout, str *value, int level )
+{
+	int fstatus;
+
+	/* ...no archivePrefix, need to handle just 'eprint' tag */
+	fstatus = fields_add( bibout, "URL", value->data, level );
+
+	if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+	else return BIBL_OK;
+}
+
+static int
+bibtexin_eprint( fields *bibin, int m, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	char *prefix;
+	int n;
+
+	/* ...do we have an archivePrefix too? */
+	n = fields_find( bibin, "ARCHIVEPREFIX", level );
+	if ( n==-1 ) n = fields_find( bibin, "EPRINTTYPE", level );
+	if ( n!=-1 ) {
+		prefix = fields_value( bibin, n, FIELDS_CHRP );
+		return process_eprint_with_prefix( bibout, prefix, invalue, level );
+	}
+
+	/* ...no we don't */
+	return process_eprint_without_prefix( bibout, invalue, level );
+}
+
+/**** bibtexin_keyword() ****/
+
+/* Split keywords="" with semicolons.
+ * Commas are also frequently used, but will break
+ * entries like:
+ *       keywords="Microscopy, Confocal"
+ * Returns BIBL_OK or BIBL_ERR_MEMERR
+ */
+
+static int
+bibtexin_keyword( fields *bibin, int m, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int fstatus, status = BIBL_OK;
+	str keyword;
+	char *p;
+
+	p = invalue->data;
+	str_init( &keyword );
+
+	while ( *p ) {
+		p = str_cpytodelim( &keyword, skip_ws( p ), ";", 1 );
+		str_trimendingws( &keyword );
+		if ( str_memerr( &keyword ) ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+		if ( keyword.len ) {
+			fstatus = fields_add( bibout, "KEYWORD", keyword.data, level );
+			if ( fstatus!=FIELDS_OK ) {
+				status = BIBL_ERR_MEMERR;
+				goto out;
+			}
+		}
+	}
+out:
+	str_free( &keyword );
+	return status;
+}
+
+/*
+ * bibtex_names( bibout, newtag, field, level);
+ *
+ * split names in author list separated by and's (use '|' character)
+ * and add names
+ *
+ * returns BIBL_OK on success, BIBL_ERR_MEMERR on memory error
+ */
+
+static int
+bibtexin_person( fields *bibin, int m, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int begin, end, ok, n, etal, i, status, match;
+	slist tokens;
+
+	/* If we match the asis or corps list add and bail. */
+	status = bibtex_matches_list( bibout, outtag, ":ASIS", invalue, level, &(pm->asis), &match );
+	if ( match==1 || status!=BIBL_OK ) return status;
+	status = bibtex_matches_list( bibout, outtag, ":CORP", invalue, level, &(pm->corps), &match );
+	if ( match==1 || status!=BIBL_OK ) return status;
+
+	slist_init( &tokens );
+
+	bibtex_split( &tokens, invalue );
+	for ( i=0; i<tokens.n; ++i )
+		bibtex_cleantoken( slist_str( &tokens, i ) );
+
+	etal = name_findetal( &tokens );
+
+	begin = 0;
+	n = tokens.n - etal;
+	while ( begin < n ) {
+
+		end = begin + 1;
+
+		while ( end < n && strcasecmp( slist_cstr( &tokens, end ), "and" ) )
+			end++;
+
+		if ( end - begin == 1 ) {
+			ok = name_addsingleelement( bibout, outtag, slist_cstr( &tokens, begin ), level, 0 );
+			if ( !ok ) { status = BIBL_ERR_MEMERR; goto out; }
+		} else {
+			ok = name_addmultielement( bibout, outtag, &tokens, begin, end, level );
+			if ( !ok ) { status = BIBL_ERR_MEMERR; goto out; }
+		}
+
+		begin = end + 1;
+
+		/* Handle repeated 'and' errors: authors="G. F. Author and and B. K. Author" */
+		while ( begin < n && !strcasecmp( slist_cstr( &tokens, begin ), "and" ) )
+			begin++;
+	}
+
+	if ( etal ) {
+		ok = name_addsingleelement( bibout, outtag, "et al.", level, 0 );
+		if ( !ok ) status = BIBL_ERR_MEMERR;
+	}
+
+out:
+	slist_free( &tokens );
+	return status;
+
+}
+
+/**** bibtexin_title() ****/
+
+/* bibtexin_titleinbook_isbooktitle()
+ *
+ * Normally, the title field of inbook refers to the book.  The
+ * section in a @inbook reference is untitled.  If it's titled,
+ * the @incollection should be used.  For example, in:
+ *
+ * @inbook{
+ *    title="xxx"
+ * }
+ *
+ * the booktitle is "xxx".
+ *
+ * However, @inbook is frequently abused (and treated like
+ * @incollection) so that title and booktitle are present
+ * and title is now 'supposed' to refer to the section.  For example:
+ *
+ * @inbook{
+ *     title="yyy",
+ *     booktitle="xxx"
+ * }
+ *
+ * Therefore report whether or not booktitle is present as well
+ * as title in @inbook references.  If not, then make 'title'
+ * correspond to the title of the book, not the section.
+ *
+ */
+static int
+bibtexin_titleinbook_isbooktitle( fields *bibin, char *intag )
+{
+	int n;
+
+	/* ...look only at 'title="xxx"' elements */
+	if ( strcasecmp( intag, "TITLE" ) ) return 0;
+
+	/* ...look only at '@inbook' references */
+	n = fields_find( bibin, "INTERNAL_TYPE", LEVEL_ANY );
+	if ( n==-1 ) return 0;
+	if ( strcasecmp( fields_value( bibin, n, FIELDS_CHRP ), "INBOOK" ) ) return 0;
+
+	/* ...look to see if 'booktitle="yyy"' exists */
+	n = fields_find( bibin, "BOOKTITLE", LEVEL_ANY );
+	if ( n==-1 ) return 0;
+	else return 1;
+}
+
+static int
+bibtexin_title( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int ok;
+
+	if ( bibtexin_titleinbook_isbooktitle( bibin, intag->data ) ) level=LEVEL_MAIN;
+	ok = title_process( bibout, "TITLE", invalue->data, level, pm->nosplittitle );
+	if ( ok ) return BIBL_OK;
+	else return BIBL_ERR_MEMERR;
+}
+
+static void
+bibtexin_notag( param *p, char *tag )
+{
+	if ( p->verbose && strcmp( tag, "INTERNAL_TYPE" ) ) {
+		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
+		fprintf( stderr, "Cannot find tag '%s'\n", tag );
+	}
+}
+
+static int
+bibtexin_convertf( fields *bibin, fields *bibout, int reftype, param *p )
+{
+	static int (*convertfns[NUM_REFTYPES])(fields *, int, str *, str *, int, param *, char *, fields *) = {
+		[ 0 ... NUM_REFTYPES-1 ] = generic_null,
+		[ SIMPLE       ] = generic_simple,
+		[ TITLE        ] = bibtexin_title,
+		[ PERSON       ] = bibtexin_person,
+		[ PAGES        ] = generic_pages,
+		[ KEYWORD      ] = bibtexin_keyword,
+		[ EPRINT       ] = bibtexin_eprint,
+		[ HOWPUBLISHED ] = bibtexin_howpublished,
+		[ LINKEDFILE   ] = bibtexin_linkedfile,
+		[ NOTES        ] = generic_notes,
+		[ BT_SENTE     ] = bibtexin_btsente,
+		[ BT_ORG       ] = bibtexin_btorg,
+		[ URL          ] = generic_url
+	};
+
+	int process, level, i, nfields, status = BIBL_OK;
+	str *intag, *invalue;
+	char *outtag;
+
+	nfields = fields_num( bibin );
+	for ( i=0; i<nfields; ++i ) {
+
+		if ( fields_used( bibin, i ) )   continue; /* e.g. successful crossref */
+		if ( fields_notag( bibin, i ) )  continue;
+		if ( fields_nodata( bibin, i ) ) continue;
+
+		intag   = fields_tag( bibin, i, FIELDS_STRP );
+		invalue = fields_value( bibin, i, FIELDS_STRP );
+
+		if ( !translate_oldtag( str_cstr( intag ), reftype, p->all, p->nall, &process, &level, &outtag ) ) {
+			bibtexin_notag( p, str_cstr( intag ) );
+			continue;
+		}
+
+		status = convertfns[ process ] ( bibin, i, intag, invalue, level, p, outtag, bibout );
+		if ( status!=BIBL_OK ) return status;
+	}
+
+	if ( status==BIBL_OK && p->verbose ) fields_report( bibout, stderr );
+
+	return status;
+}
diff --git a/bibutils/bibtexin.h b/bibutils/bibtexin.h
deleted file mode 100644
--- a/bibutils/bibtexin.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * bibtexin.h
- *
- * Copyright (c) Chris Putnam 2003-2014
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef BIBTEXIN_H
-#define BIBTEXIN_H
-
-#include "newstr.h"
-#include "list.h"
-#include "fields.h"
-#include "bibl.h"
-#include "bibutils.h"
-#include "reftypes.h"
-
-extern int  bibtexin_convertf( fields *bibin, fields *info, int reftype, param *p, variants *all, int nall );
-extern int  bibtexin_processf( fields *bibin, char *data, char *filename, long nref );
-extern int  bibtexin_cleanf( bibl *bin, param *p );
-extern int  bibtexin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset );
-extern int  bibtexin_typef( fields *bibin, char *filename, int nrefs,
-        param *p, variants *all, int nall );
-extern void bibtexin_initparams( param *p, const char *progname );
-
-
-extern variants bibtex_all[];
-extern int bibtex_nall;
-
-
-#endif
-
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-2014
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -10,16 +10,19 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
-#include "newstr.h"
+#include "str.h"
 #include "strsearch.h"
 #include "utf8.h"
 #include "xml.h"
 #include "fields.h"
 #include "name.h"
-#include "bibl.h"
-#include "doi.h"
-#include "bibtexout.h"
+#include "title.h"
+#include "url.h"
+#include "bibformats.h"
 
+static int  bibtexout_write( fields *in, FILE *fp, param *p, unsigned long refnum );
+static void bibtexout_writeheader( FILE *outptr, param *p );
+
 void
 bibtexout_initparams( param *p, const char *progname )
 {
@@ -59,44 +62,22 @@
 	TYPE_MANUAL,
 	TYPE_UNPUBLISHED,
 	TYPE_ELECTRONIC,
-	TYPE_MISC
+	TYPE_MISC,
+	NUM_TYPES
 };
 
-static void
-output_citekey( FILE *fp, fields *info, unsigned long refnum, int format_opts )
-{
-	int n = fields_find( info, "REFNUM", -1 );
-	char *p;
-	if ( n!=-1 ) {
-		p = info->data[n].data;
-		while ( p && *p && *p!='|' ) {
-			if ( format_opts & BIBOUT_STRICTKEY ) {
-				if ( isdigit((unsigned char)*p) || (*p>='A' && *p<='Z') ||
-				     (*p>='a' && *p<='z' ) )
-					fprintf( fp, "%c", *p );
-			}
-			else {
-				if ( *p!=' ' && *p!='\t' ) {
-					fprintf( fp, "%c", *p );
-				}
-			}
-			p++;
-		}
-	}
-}
-
 static int
-bibtexout_type( fields *info, char *filename, int refnum, param *p )
+bibtexout_type( fields *in, char *filename, int refnum, param *p )
 {
-	char *genre;
 	int type = TYPE_UNKNOWN, i, maxlevel, n, level;
+	char *tag, *genre;
 
 	/* determine bibliography type */
-	for ( i=0; i<info->n; ++i ) {
-		if ( strcasecmp( info->tag[i].data, "GENRE" ) &&
-		     strcasecmp( info->tag[i].data, "NGENRE" ) ) continue;
-		genre = info->data[i].data;
-		level = info->level[i];
+	for ( i=0; i<in->n; ++i ) {
+		tag = fields_tag( in, i, FIELDS_CHRP );
+		if ( strcasecmp( tag, "GENRE" ) && strcasecmp( tag, "NGENRE" ) ) continue;
+		genre = fields_value( in, i, FIELDS_CHRP );
+		level = in->level[i];
 		if ( !strcasecmp( genre, "periodical" ) ||
 		     !strcasecmp( genre, "academic journal" ) ||
 		     !strcasecmp( genre, "magazine" ) ||
@@ -115,9 +96,11 @@
 			else type = TYPE_INCOLLECTION;
 		} else if ( !strcasecmp( genre, "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;
+			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" ) )
@@ -128,26 +111,27 @@
 			type = TYPE_ELECTRONIC;
 	}
 	if ( type==TYPE_UNKNOWN ) {
-		for ( i=0; i<info->n; ++i ) {
-			if ( strcasecmp( info->tag[i].data, "ISSUANCE" ) ) continue;
-			if ( !strcasecmp( info->data[i].data, "monographic" ) ) {
-				if ( info->level[i]==0 ) type = TYPE_BOOK;
-				else if ( info->level[i]==1 ) type=TYPE_INBOOK;
+		for ( i=0; i<in->n; ++i ) {
+			tag = fields_tag( in, i, FIELDS_CHRP );
+			if ( strcasecmp( tag, "ISSUANCE" ) ) continue;
+			genre = fields_value( in, i, FIELDS_CHRP );
+			if ( !strcasecmp( genre, "monographic" ) ) {
+				if ( in->level[i]==0 ) type = TYPE_BOOK;
+				else if ( in->level[i]==1 ) type = TYPE_MISC;
 			}
 		}
 	}
 
-	/* default to BOOK type */
+	/* default to TYPE_MISC */
 	if ( type==TYPE_UNKNOWN ) {
-		maxlevel = fields_maxlevel( info );
-		if ( maxlevel > 0 ) type = TYPE_INBOOK;
+		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 );
-			n = fields_find( info, "REFNUM", -1 );
+			fprintf( stderr, "Cannot identify TYPE in reference %d ", refnum+1 );
+			n = fields_find( in, "REFNUM", LEVEL_ANY );
 			if ( n!=-1 ) 
-				fprintf( stderr, " %s", info->data[n].data);
+				fprintf( stderr, " %s", (char*) fields_value( in, n, FIELDS_CHRP ) );
 			fprintf( stderr, " (defaulting to @Misc)\n" );
 			type = TYPE_MISC;
 		}
@@ -156,355 +140,592 @@
 }
 
 static void
-output_type( FILE *fp, int type, int format_opts )
+output( FILE *fp, fields *out, int format_opts )
 {
-	typedef struct {
-		int bib_type;
-		char *type_name;
-	} typenames;
+	int i, j, len, nquotes;
+	char *tag, *value, ch;
 
-	typenames types[] = {
-		{ TYPE_ARTICLE, "Article" },
-		{ TYPE_INBOOK, "Inbook" },
-		{ TYPE_PROCEEDINGS, "Proceedings" },
-		{ TYPE_INPROCEEDINGS, "InProceedings" },
-		{ TYPE_BOOK, "Book" },
-		{ TYPE_PHDTHESIS, "PhdThesis" },
-		{ TYPE_MASTERSTHESIS, "MastersThesis" },
-		{ TYPE_REPORT, "TechReport" },
-		{ TYPE_MANUAL, "Manual" },
-		{ TYPE_COLLECTION, "Collection" },
-		{ TYPE_INCOLLECTION, "InCollection" },
-		{ TYPE_UNPUBLISHED, "Unpublished" },
-		{ TYPE_ELECTRONIC, "Electronic" },
-		{ TYPE_MISC, "Misc" } };
-	int i, len, ntypes = sizeof( types ) / sizeof( types[0] );
-	char *s = NULL;
-	for ( i=0; i<ntypes; ++i ) {
-		if ( types[i].bib_type == type ) {
-			s = types[i].type_name;
-			break;
-		}
-	}
-	if ( !s ) s = types[ntypes-1].type_name; /* default to TYPE_MISC */
-	if ( !(format_opts & BIBOUT_UPPERCASE ) ) fprintf( fp, "@%s{", s );
+	/* ...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( s );
+		len = strlen( value );
 		fprintf( fp, "@" );
 		for ( i=0; i<len; ++i )
-			fprintf( fp, "%c", toupper((unsigned char)s[i]) );
+			fprintf( fp, "%c", toupper((unsigned char)value[i]) );
 		fprintf( fp, "{" );
 	}
-}
 
-static void
-output_element( FILE *fp, char *tag, char *data, int format_opts )
-{
-	int i, len, nquotes = 0;
-	char ch;
-	fprintf( fp, ",\n" );
-	if ( format_opts & BIBOUT_WHITESPACE ) fprintf( fp, "  " );
-	if ( !(format_opts & 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 & BIBOUT_WHITESPACE ) fprintf( fp, " = \t" );
-	else fprintf( fp, "=" );
-
-	if ( format_opts & BIBOUT_BRACKETS ) fprintf( fp, "{" );
-	else fprintf( fp, "\"" );
+	/* ...output refnum "Smith2001" */
+	value = ( char * ) fields_value( out, 1, FIELDS_CHRP );
+	fprintf( fp, "%s", value );
 
-	len = strlen( data );
-	for ( i=0; i<len; ++i ) {
-		ch = data[i];
-		if ( ch!='\"' ) fprintf( fp, "%c", ch );
+	/* ...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 {
-			if ( format_opts & BIBOUT_BRACKETS || 
-			    ( i>0 && data[i-1]=='\\' ) )
-				fprintf( fp, "\"" );
+			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 ( nquotes % 2 == 0 )
-					fprintf( fp, "``" );
-				else    fprintf( fp, "\'\'" );
-				nquotes++;
+				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, "\"" );
 	}
 
-	if ( format_opts & 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
-output_and_use( FILE *fp, fields *info, int n, char *outtag, int format_opts )
+append_type( int type, fields *out, int *status )
 {
-	output_element( fp, outtag, info->data[n].data, format_opts );
-	fields_setused( info, n );
+	char *typenames[ NUM_TYPES ] = {
+		[ TYPE_ARTICLE       ] = "Article",
+		[ TYPE_INBOOK        ] = "Inbook",
+		[ TYPE_PROCEEDINGS   ] = "Proceedings",
+		[ TYPE_INPROCEEDINGS ] = "InProceedings",
+		[ TYPE_BOOK          ] = "Book",
+		[ TYPE_PHDTHESIS     ] = "PhdThesis",
+		[ TYPE_MASTERSTHESIS ] = "MastersThesis",
+		[ TYPE_REPORT        ] = "TechReport",
+		[ TYPE_MANUAL        ] = "Manual",
+		[ TYPE_COLLECTION    ] = "Collection",
+		[ TYPE_INCOLLECTION  ] = "InCollection",
+		[ TYPE_UNPUBLISHED   ] = "Unpublished",
+		[ TYPE_ELECTRONIC    ] = "Electronic",
+		[ TYPE_MISC          ] = "Misc",
+	};
+	int fstatus;
+	char *s;
+
+	if ( type < 0 || type >= NUM_TYPES ) type = TYPE_MISC;
+	s = typenames[ type ];
+
+	fstatus = fields_add( out, "TYPE", s, LEVEL_MAIN );
+	if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 }
 
 static void
-output_simple( FILE *fp, fields *info, char *intag, char *outtag, 
-		int format_opts )
+append_citekey( fields *in, fields *out, int format_opts, int *status )
 {
-	int n = fields_find( info, intag, -1 );
+	int n, fstatus;
+	str s;
+	char *p;
+
+	n = fields_find( in, "REFNUM", LEVEL_ANY );
+	if ( ( format_opts & BIBL_FORMAT_BIBOUT_DROPKEY ) || n==-1 ) {
+		fstatus = fields_add( out, "REFNUM", "", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+
+	else {
+		str_init( &s );
+		p = fields_value( in, n, FIELDS_CHRP );
+		while ( p && *p && *p!='|' ) {
+			if ( format_opts & BIBL_FORMAT_BIBOUT_STRICTKEY ) {
+				if ( isdigit((unsigned char)*p) || (*p>='A' && *p<='Z') ||
+				     (*p>='a' && *p<='z' ) ) {
+					str_addchar( &s, *p );
+				}
+			}
+			else {
+				if ( *p!=' ' && *p!='\t' ) {
+					str_addchar( &s, *p );
+				}
+			}
+			p++;
+		}
+		if ( str_memerr( &s ) )  { *status = BIBL_ERR_MEMERR; str_free( &s ); return; }
+		fstatus = fields_add( out, "REFNUM", str_cstr( &s ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+		str_free( &s );
+	}
+}
+
+static void
+append_simple( fields *in, char *intag, char *outtag, fields *out, int *status )
+{
+	int n, fstatus;
+
+	n = fields_find( in, intag, LEVEL_ANY );
 	if ( n!=-1 ) {
-		output_and_use( fp, info, n, outtag, format_opts );
+		fields_setused( in, n );
+		fstatus = fields_add( out, outtag, fields_value( in, n, FIELDS_CHRP ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	}
 }
 
 static void
-output_simpleall( FILE *fp, fields *info, char *intag, char *outtag,
-		int format_opts )
+append_simpleall( fields *in, char *intag, char *outtag, fields *out, int *status )
 {
-	int i;
-	for ( i=0; i<info->n; ++i ) {
-		if ( fields_match_tag( info, i, intag ) )
-			output_and_use( fp, info, i, outtag, format_opts );
+	int i, fstatus;
+
+	for ( i=0; i<in->n; ++i ) {
+		if ( fields_match_tag( in, i, intag ) ) {
+			fields_setused( in, i );
+			fstatus = fields_add( out, outtag, fields_value( in, i, FIELDS_CHRP ), LEVEL_MAIN );
+			if ( fstatus!=FIELDS_OK ) {
+				*status = BIBL_ERR_MEMERR;
+				return;
+			}
+		}
 	}
 }
 
 static void
-output_keywords( FILE *fp, fields *info, int format_opts )
+append_keywords( fields *in, fields *out, int *status )
 {
-	newstr keywords, *word;
+	str keywords, *word;
+	vplist_index i;
+	int fstatus;
 	vplist a;
-	int i;
-	newstr_init( &keywords );
+
+	str_init( &keywords );
 	vplist_init( &a );
-	fields_findv_each( info, LEVEL_ANY, FIELDS_STRP, &a, "KEYWORD" );
+
+	fields_findv_each( in, LEVEL_ANY, FIELDS_STRP, &a, "KEYWORD" );
+
 	if ( a.n ) {
+
 		for ( i=0; i<a.n; ++i ) {
 			word = vplist_get( &a, i );
-			if ( i>0 ) newstr_strcat( &keywords, "; " );
-			newstr_newstrcat( &keywords, word );
+			if ( i>0 ) str_strcatc( &keywords, "; " );
+			str_strcat( &keywords, word );
 		}
-		output_element( fp, "keywords", keywords.data, format_opts );
 
+		if ( str_memerr( &keywords ) ) { *status = BIBL_ERR_MEMERR; goto out; }
+
+		fstatus = fields_add( out, "keywords", str_cstr( &keywords ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			*status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+
+
 	}
-	newstr_free( &keywords );
+
+out:
+	str_free( &keywords );
 	vplist_free( &a );
 }
 
 static void
-output_fileattach( FILE *fp, fields *info, int format_opts )
+append_fileattach( fields *in, fields *out, int *status )
 {
-	newstr data;
-	int i;
-	newstr_init( &data );
-	for ( i=0; i<info->n; ++i ) {
-		if ( strcasecmp( info->tag[i].data, "FILEATTACH" ) ) continue;
-		newstr_strcpy( &data, ":" );
-		newstr_newstrcat( &data, &(info->data[i]) );
-		if ( strsearch( info->data[i].data, ".pdf" ) )
-			newstr_strcat( &data, ":PDF" );
-		else if ( strsearch( info->data[i].data, ".html" ) )
-			newstr_strcat( &data, ":HTML" );
-		else newstr_strcat( &data, ":TYPE" );
-		output_element( fp, "file", data.data, format_opts );
-		fields_setused( info, i );
-		newstr_empty( &data );
+	char *tag, *value;
+	int i, fstatus;
+	str data;
+
+	str_init( &data );
+
+	for ( i=0; i<in->n; ++i ) {
+
+		tag = fields_tag( in, i, FIELDS_CHRP );
+		if ( strcasecmp( tag, "FILEATTACH" ) ) continue;
+
+		value = fields_value( in, i, FIELDS_CHRP );
+		str_strcpyc( &data, ":" );
+		str_strcatc( &data, value );
+		if ( strsearch( value, ".pdf" ) )
+			str_strcatc( &data, ":PDF" );
+		else if ( strsearch( value, ".html" ) )
+			str_strcatc( &data, ":HTML" );
+		else str_strcatc( &data, ":TYPE" );
+
+		if ( str_memerr( &data ) ) {
+			*status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+
+		fields_setused( in, i );
+		fstatus = fields_add( out, "file", str_cstr( &data ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			*status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+
+		str_empty( &data );
 	}
-	newstr_free( &data );
+out:
+	str_free( &data );
 }
 
 static void
-output_people( FILE *fp, fields *info, unsigned long refnum, char *tag, 
-		char *ctag, char *atag, char *bibtag, int level, 
-		int format_opts )
+append_people( fields *in, char *tag, char *ctag, char *atag,
+		char *bibtag, int level, fields *out, int format_opts )
 {
-	newstr allpeople, oneperson;
+	str allpeople, oneperson;
 	int i, npeople, person, corp, asis;
 
-	newstrs_init( &allpeople, &oneperson, NULL );
+	strs_init( &allpeople, &oneperson, NULL );
 
 	/* primary citation authors */
 	npeople = 0;
-	for ( i=0; i<info->n; ++i ) {
-		if ( level!=-1 && info->level[i]!=level ) continue;
-		person = ( strcasecmp( info->tag[i].data, tag ) == 0 );
-		corp   = ( strcasecmp( info->tag[i].data, ctag ) == 0 );
-		asis   = ( strcasecmp( info->tag[i].data, atag ) == 0 );
+	for ( i=0; i<in->n; ++i ) {
+		if ( level!=LEVEL_ANY && in->level[i]!=level ) continue;
+		person = ( strcasecmp( in->tag[i].data, tag ) == 0 );
+		corp   = ( strcasecmp( in->tag[i].data, ctag ) == 0 );
+		asis   = ( strcasecmp( in->tag[i].data, atag ) == 0 );
 		if ( person || corp || asis ) {
 			if ( npeople>0 ) {
-				if ( format_opts & BIBOUT_WHITESPACE )
-					newstr_strcat(&allpeople,"\n\t\tand ");
-				else newstr_strcat( &allpeople, "\nand " );
+				if ( format_opts & BIBL_FORMAT_BIBOUT_WHITESPACE )
+					str_strcatc( &allpeople, "\n\t\tand " );
+				else str_strcatc( &allpeople, "\nand " );
 			}
 			if ( corp ) {
-				newstr_addchar( &allpeople, '{' );
-				newstr_strcat( &allpeople, fields_value( info, i, FIELDS_CHRP ) );
-				newstr_addchar( &allpeople, '}' );
+				str_addchar( &allpeople, '{' );
+				str_strcat( &allpeople, fields_value( in, i, FIELDS_STRP ) );
+				str_addchar( &allpeople, '}' );
 			} else if ( asis ) {
-				newstr_addchar( &allpeople, '{' );
-				newstr_strcat( &allpeople, fields_value( info, i, FIELDS_CHRP ) );
-				newstr_addchar( &allpeople, '}' );
+				str_addchar( &allpeople, '{' );
+				str_strcat( &allpeople, fields_value( in, i, FIELDS_STRP ) );
+				str_addchar( &allpeople, '}' );
 			} else {
-				name_build_withcomma( &oneperson, fields_value( info, i, FIELDS_CHRP ) );
-				newstr_newstrcat( &allpeople, &oneperson );
+				name_build_withcomma( &oneperson, fields_value( in, i, FIELDS_CHRP ) );
+				str_strcat( &allpeople, &oneperson );
 			}
 			npeople++;
 		}
 	}
-	if ( npeople )
-		output_element( fp, bibtag, allpeople.data, format_opts );
+	if ( npeople ) {
+		fields_add( out, bibtag, allpeople.data, LEVEL_MAIN );
+	}
 
-	newstrs_free( &allpeople, &oneperson, NULL );
+	strs_free( &allpeople, &oneperson, NULL );
 }
 
-static void
-output_title( FILE *fp, fields *info, unsigned long refnum, char *bibtag, int level, int format_opts )
+static int
+append_title_chosen( fields *in, char *bibtag, fields *out, int nmainttl, int nsubttl )
 {
-	newstr title, *mainttl, *subttl;
-	int n1 = -1, n2 = -1;
-	/* Option is for short titles of journals */
-	if ( ( format_opts & BIBOUT_SHORTTITLE ) && level==1 ) {
-		n1 = fields_find( info, "SHORTTITLE", level );
-		n2 = fields_find( info, "SHORTSUBTITLE", level );
+	str fulltitle, *mainttl = NULL, *subttl = NULL;
+	int status, ret = BIBL_OK;
+
+	str_init( &fulltitle );
+
+	if ( nmainttl!=-1 ) {
+		mainttl = fields_value( in, nmainttl, FIELDS_STRP );
+		fields_setused( in, nmainttl );
 	}
-	if ( n1==-1 ) {
-		n1 = fields_find( info, "TITLE", level );
-		n2 = fields_find( info, "SUBTITLE", level );
+
+	if ( nsubttl!=-1 ) {
+		subttl = fields_value( in, nsubttl, FIELDS_STRP );
+		fields_setused( in, nsubttl );
 	}
-	if ( n1!=-1 ) {
-		newstr_init( &title );
-		mainttl = fields_value( info, n1, FIELDS_STRP );
-		newstr_newstrcpy( &title, mainttl );
-		fields_setused( info, n1 );
-		if ( n2!=-1 ) {
-			subttl = fields_value( info, n2, FIELDS_STRP );
-			if ( mainttl->len > 0 &&
-			     mainttl->data[mainttl->len-1]!='?' )
-				newstr_strcat( &title, ": " );
-			else newstr_addchar( &title, ' ' );
-			newstr_newstrcat( &title, subttl );
-			fields_setused( info, n2 );
-		}
-		output_element( fp, bibtag, title.data, format_opts );
-		newstr_free( &title );
+
+	title_combine( &fulltitle, mainttl, subttl );
+
+	if ( str_memerr( &fulltitle ) ) {
+		ret = BIBL_ERR_MEMERR;
+		goto out;
 	}
+
+	if ( str_has_value( &fulltitle ) ) {
+		status = fields_add( out, bibtag, str_cstr( &fulltitle ), LEVEL_MAIN );
+		if ( status!=FIELDS_OK ) ret = BIBL_ERR_MEMERR;
+	}
+
+out:
+	str_free( &fulltitle );
+	return ret;
 }
 
+static int
+append_title( fields *in, char *bibtag, int level, fields *out, int format_opts )
+{
+	int title = -1,     short_title = -1;
+	int subtitle = -1,  short_subtitle = -1;
+	int use_title = -1, use_subtitle = -1;
+
+	title          = fields_find( in, "TITLE",         level );
+	short_title    = fields_find( in, "SHORTTITLE",    level );
+	subtitle       = fields_find( in, "SUBTITLE",      level );
+	short_subtitle = fields_find( in, "SHORTSUBTITLE", level );
+
+	if ( title==-1 || ( ( format_opts & BIBL_FORMAT_BIBOUT_SHORTTITLE ) && level==1 ) ) {
+		use_title    = short_title;
+		use_subtitle = short_subtitle;
+	}
+
+	else {
+		use_title    = title;
+		use_subtitle = subtitle;
+	}
+
+	return append_title_chosen( in, bibtag, out, use_title, use_subtitle );
+}
+
 static void
-output_date( FILE *fp, fields *info, unsigned long refnum, int format_opts )
+append_titles( fields *in, int type, fields *out, int format_opts, int *status )
 {
+	/* item=main level title */
+	*status = append_title( in, "title", 0, out, format_opts );
+	if ( *status!=BIBL_OK ) return;
+
+	switch( type ) {
+
+		case TYPE_ARTICLE:
+		*status = append_title( in, "journal", 1, out, format_opts );
+		break;
+
+		case TYPE_INBOOK:
+		*status = append_title( in, "bookTitle", 1, out, format_opts );
+		if ( *status!=BIBL_OK ) return;
+		*status = append_title( in, "series",    2, out, format_opts );
+		break;
+
+		case TYPE_INCOLLECTION:
+		case TYPE_INPROCEEDINGS:
+		*status = append_title( in, "booktitle", 1, out, format_opts );
+		if ( *status!=BIBL_OK ) return;
+		*status = append_title( in, "series",    2, out, format_opts );
+		break;
+
+		case TYPE_PHDTHESIS:
+		case TYPE_MASTERSTHESIS:
+		*status = append_title( in, "series", 1, out, format_opts );
+		break;
+
+		case TYPE_BOOK:
+		case TYPE_REPORT:
+		case TYPE_COLLECTION:
+		case TYPE_PROCEEDINGS:
+		*status = append_title( in, "series", 1, out, format_opts );
+		if ( *status!=BIBL_OK ) return;
+		*status = append_title( in, "series", 2, out, format_opts );
+		break;
+
+		default:
+		/* do nothing */
+		break;
+
+	}
+}
+
+static int
+find_date( fields *in, char *date_element )
+{
+	char date[100], partdate[100];
+	int n;
+
+	sprintf( date, "DATE:%s", date_element );
+	n = fields_find( in, date, LEVEL_ANY );
+
+	if ( n==-1 ) {
+		sprintf( partdate, "PARTDATE:%s", date_element );
+		n = fields_find( in, partdate, LEVEL_ANY );
+	}
+
+	return n;
+}
+
+static void
+append_date( fields *in, fields *out, int *status )
+{
 	char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", 
 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
-	int n, month;
-	n = fields_find( info, "YEAR", -1 );
-	if ( n==-1 ) n = fields_find( info, "PARTYEAR", -1 );
+	int n, month, fstatus;
+
+	n = find_date( in, "YEAR" );
 	if ( n!=-1 ) {
-		output_element( fp, "year", info->data[n].data, format_opts );
-		fields_setused( info, n );
+		fields_setused( in, n );
+		fstatus = fields_add( out, "year", in->data[n].data, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			*status = BIBL_ERR_MEMERR;
+			return;
+		}
 	}
-	n = fields_find( info, "MONTH", -1 );
-	if ( n==-1 ) n = fields_find( info, "PARTMONTH", -1 );
+
+	n = find_date( in, "MONTH" );
 	if ( n!=-1 ) {
-		month = atoi( info->data[n].data );
+		fields_setused( in, n );
+		month = atoi( in->data[n].data );
 		if ( month>0 && month<13 )
-			output_element( fp, "month", months[month-1], format_opts );
+			fstatus = fields_add( out, "month", months[month-1], LEVEL_MAIN );
 		else
-			output_element( fp, "month", info->data[n].data, format_opts );
-		fields_setused( info, n );
+			fstatus = fields_add( out, "month", in->data[n].data, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			*status = BIBL_ERR_MEMERR;
+			return;
+		}
 	}
-	n = fields_find( info, "DAY", -1 );
-	if ( n==-1 ) n = fields_find( info, "PARTDAY", -1 );
+
+	n = find_date( in, "DAY" );
 	if ( n!=-1 ) {
-		output_element( fp, "day", info->data[n].data, format_opts );
-		fields_setused( info, n );
+		fields_setused( in, n );
+		fstatus = fields_add( out, "day", in->data[n].data, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			*status = BIBL_ERR_MEMERR;
+			return;
+		}
 	}
-}
 
+}
 
-/* output article number as pages if true pages aren't found */
 static void
-output_articlenumber( FILE *fp, fields *info, unsigned long refnum,
-	int format_opts )
+append_arxiv( fields *in, fields *out, int *status )
 {
-	int ar = fields_find( info, "ARTICLENUMBER", -1 );
-	if ( ar!=-1 ) {
-		newstr pages;
-		newstr_init( &pages );
-		newstr_strcat( &pages, info->data[ar].data );
-		output_element( fp, "pages", pages.data, format_opts );
-		fields_setused( info, ar );
-		newstr_free( &pages );
+	int n, fstatus1, fstatus2;
+	str url;
+
+	n = fields_find( in, "ARXIV", LEVEL_ANY );
+	if ( n==-1 ) return;
+
+	fields_setused( in, n );
+
+	/* ...write:
+	 *     archivePrefix = "arXiv",
+	 *     eprint = "#####",
+	 * ...for arXiv references
+	 */
+	fstatus1 = fields_add( out, "archivePrefix", "arXiv", LEVEL_MAIN );
+	fstatus2 = fields_add( out, "eprint", fields_value( in, n, FIELDS_CHRP ), LEVEL_MAIN );
+	if ( fstatus1!=FIELDS_OK || fstatus2!=FIELDS_OK ) {
+		*status = BIBL_ERR_MEMERR;
+		return;
 	}
+
+	/* ...also write:
+	 *     url = "http://arxiv.org/abs/####",
+	 * ...to maximize compatibility
+	 */
+	str_init( &url );
+	arxiv_to_url( in, n, "URL", &url );
+	if ( str_has_value( &url ) ) {
+		fstatus1 = fields_add( out, "url", str_cstr( &url ), LEVEL_MAIN );
+		if ( fstatus1!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+	str_free( &url );
 }
 
 static void
-output_arxiv( FILE *fp, fields *info, int format_opts )
+append_urls( fields *in, fields *out, int *status )
 {
-	int ar = fields_find( info, "ARXIV", -1 );
-	if ( ar!=-1 ) {
-		newstr arxiv;
-		newstr_init( &arxiv );
-		arxiv_to_url( info, ar, "URL", &arxiv );
-		if ( arxiv.len )
-			output_element( fp, "url", arxiv.data, format_opts );
-		newstr_free( &arxiv );
+	int lstatus;
+	slist types;
+
+	lstatus = slist_init_valuesc( &types, "URL", "DOI", "PMID", "PMC", "JSTOR", NULL );
+	if ( lstatus!=SLIST_OK ) {
+		*status = BIBL_ERR_MEMERR;
+		return;
 	}
+
+	*status = urls_merge_and_add( in, LEVEL_ANY, out, "url", LEVEL_MAIN, &types );
+
+	slist_free( &types );
 }
 
 static void
-output_pmid( FILE *fp, fields *info, int format_opts )
+append_isi( fields *in, fields *out, int *status )
 {
-	int pm = fields_find( info, "PMID", -1 );
-	if ( pm!=-1 ) {
-		newstr pmid;
-		newstr_init( &pmid );
-		pmid_to_url( info, pm, "URL", &pmid );
-		if ( pmid.len )
-			output_element( fp, "url", pmid.data, format_opts );
-		newstr_free( &pmid );
+	int n, fstatus;
+
+	n = fields_find( in, "ISIREFNUM", LEVEL_ANY );
+	if ( n!=-1 ) {
+		fstatus = fields_add( out, "note", fields_value( in, n, FIELDS_CHRP ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	}
 }
 
-static void
-output_jstor( FILE *fp, fields *info, int format_opts )
+static int
+append_articlenumber( fields *in, fields *out )
 {
-	int js = fields_find( info, "JSTOR", -1 );
-	if ( js!=-1 ) {
-		newstr jstor;
-		newstr_init( &jstor );
-		jstor_to_url( info, js, "URL", &jstor );
-		if ( jstor.len )
-			output_element( fp, "url", jstor.data, format_opts );
-		newstr_free( &jstor );
+	int n, fstatus;
+
+	n = fields_find( in, "ARTICLENUMBER", LEVEL_ANY );
+	if ( n!=-1 ) {
+		fields_setused( in, n );
+		fstatus = fields_add( out, "pages", fields_value( in, n, FIELDS_CHRP ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
+	return BIBL_OK;
 }
 
-static void
-output_pages( FILE *fp, fields *info, unsigned long refnum, int format_opts )
+static int
+pages_build_pagestr( str *pages, fields *in, int sn, int en, int format_opts )
 {
-	newstr pages;
-	int sn, en;
-	sn = fields_find( info, "PAGESTART", -1 );
-	en = fields_find( info, "PAGEEND", -1 );
-	if ( sn==-1 && en==-1 ) {
-		output_articlenumber( fp, info, refnum, format_opts );
-		return;
-	}
-	newstr_init( &pages );
+	/* ...append if starting page number is defined */
 	if ( sn!=-1 ) {
-		newstr_strcat( &pages, info->data[sn].data );
-		fields_setused( info, sn );
+		str_strcat( pages, fields_value( in, sn, FIELDS_STRP ) );
+		fields_setused( in, sn );
 	}
+
+	/* ...append dashes if both starting and ending page numbers are defined */
 	if ( sn!=-1 && en!=-1 ) {
-		if ( format_opts & BIBOUT_SINGLEDASH ) 
-			newstr_strcat( &pages, "-" );
+		if ( format_opts & BIBL_FORMAT_BIBOUT_SINGLEDASH )
+			str_strcatc( pages, "-" );
 		else
-			newstr_strcat( &pages, "--" );
+			str_strcatc( pages, "--" );
 	}
+
+	/* ...append ending page number is defined */
 	if ( en!=-1 ) {
-		newstr_strcat( &pages, info->data[en].data );
-		fields_setused( info, en );
+		str_strcat( pages, fields_value( in, en, FIELDS_STRP ) );
+		fields_setused( in, en );
 	}
-	output_element( fp, "pages", pages.data, format_opts );
-	newstr_free( &pages );
+
+	if ( str_memerr( pages ) ) return BIBL_ERR_MEMERR;
+	else return BIBL_OK;
 }
 
+static int
+pages_are_defined( fields *in, int *sn, int *en )
+{
+	*sn = fields_find( in, "PAGES:START", LEVEL_ANY );
+	*en = fields_find( in, "PAGES:STOP",  LEVEL_ANY );
+	if ( *sn==-1 && *en==-1 ) return 0;
+	else return 1;
+}
+
+static void
+append_pages( fields *in, fields *out, int format_opts, int *status )
+{
+	int sn, en, fstatus;
+	str pages;
+
+	if ( !pages_are_defined( in, &sn, &en ) ) {
+		*status = append_articlenumber( in, out );
+		return;
+	}
+
+	str_init( &pages );
+	*status = pages_build_pagestr( &pages, in, sn, en, format_opts );
+	if ( *status==BIBL_OK ) {
+		fstatus = fields_add( out, "pages", str_cstr( &pages ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+	str_free( &pages );
+}
+
 /*
  * from Tim Hicks:
  * I'm no expert on bibtex, but those who know more than I on our mailing 
@@ -518,108 +739,94 @@
  */
 
 static void
-output_issue_number( FILE *fp, fields *info, int format_opts )
+append_issue_number( fields *in, fields *out, int *status )
 {
-	int nissue  = fields_find( info, "ISSUE", -1 );
-	int nnumber = fields_find( info, "NUMBER", -1 );
+	int nissue  = fields_find( in, "ISSUE",  LEVEL_ANY );
+	int nnumber = fields_find( in, "NUMBER", LEVEL_ANY );
+	int fstatus;
+
 	if ( nissue!=-1 && nnumber!=-1 ) {
-		output_and_use( fp, info, nissue,  "issue",  format_opts );
-		output_and_use( fp, info, nnumber, "number", format_opts );
-/*		output_element( fp, "issue", info->data[nissue].data, 
-				format_opts );
-		fields_setused( info, nissue );
-		output_element( fp, "number", info->data[nnumber].data, 
-				format_opts );
-		fields_setused( info, nnumber );*/
+		fields_setused( in, nissue );
+		fields_setused( in, nnumber );
+		fstatus = fields_add( out, "issue", fields_value( in, nissue, FIELDS_CHRP ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			*status = BIBL_ERR_MEMERR;
+			return;
+		}
+		fstatus = fields_add( out, "number", fields_value( in, nnumber, FIELDS_CHRP ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	} else if ( nissue!=-1 ) {
-		output_and_use( fp, info, nissue, "number", format_opts );
-/*
-		output_element( fp, "number", info->data[nissue].data, 
-				format_opts );
-		fields_setused( info, nissue );*/
+		fields_setused( in, nissue );
+		fstatus = fields_add( out, "number", fields_value( in, nissue, FIELDS_CHRP ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	} else if ( nnumber!=-1 ) {
-		output_and_use( fp, info, nnumber, "number", format_opts );
-/*
-		output_element( fp, "number", info->data[nnumber].data, 
-				format_opts );
-		fields_setused( info, nnumber );
-*/
+		fields_setused( in, nnumber );
+		fstatus = fields_add( out, "number", fields_value( in, nnumber, FIELDS_CHRP ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	}
 }
 
-void
-bibtexout_write( fields *info, FILE *fp, param *p, unsigned long refnum )
+static int
+append_data( fields *in, fields *out, param *p, unsigned long refnum )
 {
-	int type;
-	fields_clearused( info );
-	type = bibtexout_type( info, "", refnum, p );
-	output_type( fp, type, p->format_opts );
-	if ( !( p->format_opts & BIBOUT_DROPKEY ) )
-		output_citekey( fp, info, refnum, p->format_opts );
-	output_people( fp, info, refnum, "AUTHOR", "AUTHOR:CORP", "AUTHOR:ASIS", "author", 0,
-		p->format_opts );
-	output_people( fp, info, refnum, "EDITOR", "EDITOR:CORP", "EDITOR:ASIS", "editor", -1,
-		p->format_opts );
-	output_people( fp, info, refnum, "TRANSLATOR", "TRANSLATOR:CORP", "TRANSLATOR:ASIS", "translator", -1, p->format_opts );
+	int type, status = BIBL_OK;
 
-	/* item=main level title */
-	if ( type==TYPE_INBOOK )
-		output_title( fp, info, refnum, "chapter", 0, p->format_opts );
-	else
-		output_title( fp, info, refnum, "title", 0, p->format_opts );
+	type = bibtexout_type( in, "", refnum, p );
 
-	/* item=host level title */
-	if ( type==TYPE_ARTICLE )
-		output_title( fp, info, refnum, "journal", 1, p->format_opts );
-	else if ( type==TYPE_INBOOK ) {
-		output_title( fp, info, refnum, "title", 1, p->format_opts );
-		output_title( fp, info, refnum, "series", 2, p->format_opts );
-	}
-	else if ( type==TYPE_INPROCEEDINGS || type==TYPE_INCOLLECTION ) {
-		output_title( fp, info, refnum, "booktitle", 1, p->format_opts );
-		output_title( fp, info, refnum, "series", 2, p->format_opts );
-	}
-	else if ( type==TYPE_PHDTHESIS || type==TYPE_MASTERSTHESIS ) {
-		output_title( fp, info, refnum, "series", 1, p->format_opts );
-	}
-	else if ( type==TYPE_BOOK || type==TYPE_COLLECTION || type==TYPE_PROCEEDINGS || type==TYPE_REPORT ) {
-		output_title( fp, info, refnum, "series", 1, p->format_opts );
-		output_title( fp, info, refnum, "series", 2, p->format_opts );
-	}
+	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_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_keywords    ( in, out, &status );
+	append_simple      ( in, "CONTENTS",           "contents",  out, &status );
+	append_simple      ( in, "ABSTRACT",           "abstract",  out, &status );
+	append_simple      ( in, "LOCATION",           "location",  out, &status );
+	append_simple      ( in, "DEGREEGRANTOR",      "school",    out, &status );
+	append_simple      ( in, "DEGREEGRANTOR:ASIS", "school",    out, &status );
+	append_simple      ( in, "DEGREEGRANTOR:CORP", "school",    out, &status );
+	append_simpleall   ( in, "NOTES",              "note",      out, &status );
+	append_simpleall   ( in, "ANNOTE",             "annote",    out, &status );
+	append_simple      ( in, "ISBN",               "isbn",      out, &status );
+	append_simple      ( in, "ISSN",               "issn",      out, &status );
+	append_simple      ( in, "MRNUMBER",           "mrnumber",  out, &status );
+	append_simple      ( in, "CODEN",              "coden",     out, &status );
+	append_simple      ( in, "DOI",                "doi",       out, &status );
+	append_urls        ( in, out, &status );
+	append_fileattach  ( in, out, &status );
+	append_arxiv       ( in, out, &status );
+	append_simple      ( in, "EPRINTCLASS",        "primaryClass", out, &status );
+	append_isi         ( in, out, &status );
+	append_simple      ( in, "LANGUAGE",           "language",  out, &status );
 
-	output_date( fp, info, refnum, p->format_opts );
-	output_simple( fp, info, "EDITION", "edition", p->format_opts );
-	output_simple( fp, info, "PUBLISHER", "publisher", p->format_opts );
-	output_simple( fp, info, "ADDRESS", "address", p->format_opts );
-	output_simple( fp, info, "VOLUME", "volume", p->format_opts );
-	output_issue_number( fp, info, p->format_opts );
-/*	output_simple( fp, info, "ISSUE", "issue", p->format_opts );
-	output_simple( fp, info, "NUMBER", "number", p->format_opts );s*/
-	output_pages( fp, info, refnum, p->format_opts );
-	output_keywords( fp, info, p->format_opts );
-	output_simple( fp, info, "CONTENTS", "contents", p->format_opts );
-	output_simple( fp, info, "ABSTRACT", "abstract", p->format_opts );
-	output_simple( fp, info, "LOCATION", "location", p->format_opts );
-	output_simple( fp, info, "DEGREEGRANTOR", "school", p->format_opts );
-	output_simple( fp, info, "DEGREEGRANTOR:ASIS", "school", p->format_opts );
-	output_simple( fp, info, "DEGREEGRANTOR:CORP", "school", p->format_opts );
-	output_simpleall( fp, info, "NOTES", "note", p->format_opts );
-	output_simpleall( fp, info, "ANNOTE", "annote", p->format_opts );
-	output_simple( fp, info, "ISBN", "isbn", p->format_opts );
-	output_simple( fp, info, "ISSN", "issn", p->format_opts );
-	output_simple( fp, info, "DOI", "doi", p->format_opts );
-	output_simpleall( fp, info, "URL", "url", p->format_opts );
-	output_fileattach( fp, info, p->format_opts );
-	output_arxiv( fp, info, p->format_opts );
-	output_pmid( fp, info, p->format_opts );
-	output_jstor( fp, info, p->format_opts );
-	output_simple( fp, info, "LANGUAGE", "language", p->format_opts );
-	if ( p->format_opts & BIBOUT_FINALCOMMA ) fprintf( fp, "," );
-	fprintf( fp, "\n}\n\n" );
-	fflush( fp );
+	return status;
 }
 
-void
+static int
+bibtexout_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, p->format_opts );
+
+	fields_free( &out );
+
+	return status;
+}
+
+static void
 bibtexout_writeheader( FILE *outptr, param *p )
 {
 	if ( p->utf8bom ) utf8_writebom( outptr );
diff --git a/bibutils/bibtexout.h b/bibutils/bibtexout.h
deleted file mode 100644
--- a/bibutils/bibtexout.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * bibtexout.h
- *
- * Copyright (c) Chris Putnam 2005-2013
- *
- * Source code released under GPL version 2
- *
- */
-#ifndef BIBTEXOUT_H
-#define BIBTEXOUT_H
-
-#include <stdio.h>
-#include "fields.h"
-#include "bibutils.h"
-
-/* bibtexout output options */
-#define BIBOUT_FINALCOMMA   (2)
-#define BIBOUT_SINGLEDASH   (4)
-#define BIBOUT_WHITESPACE   (8)
-#define BIBOUT_BRACKETS    (16)
-#define BIBOUT_UPPERCASE   (32)
-#define BIBOUT_STRICTKEY   (64)
-#define BIBOUT_SHORTTITLE (128)
-#define BIBOUT_DROPKEY    (256)
-
-extern void bibtexout_write( fields *info, FILE *fp, param *p, 
-		unsigned long refnum );
-extern void bibtexout_writeheader( FILE *outptr, param *p );
-extern void bibtexout_initparams( param *p, const char *progname );
-
-#endif
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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -16,38 +16,44 @@
  * Journal Article
  */
 static lookups article[] = {
-	{ "author",       "AUTHOR",    PERSON, LEVEL_MAIN },
-	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "title",        "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "year",         "PARTYEAR",  SIMPLE, LEVEL_MAIN },
-	{ "month",        "PARTMONTH", SIMPLE, LEVEL_MAIN },
-	{ "day",          "PARTDAY",   SIMPLE, LEVEL_MAIN },
-	{ "volume",       "VOLUME",    SIMPLE, LEVEL_MAIN },
-	{ "pages",        "PAGES",     PAGES,  LEVEL_MAIN },
-	{ "number",       "ISSUE",     SIMPLE, LEVEL_MAIN },
-	{ "issue",        "ISSUE",     SIMPLE, LEVEL_MAIN },
-	{ "journal",      "TITLE",     TITLE,  LEVEL_HOST },
-	{ "publisher",    "PUBLISHER", SIMPLE, LEVEL_HOST },
-	{ "address",      "ADDRESS",   SIMPLE, LEVEL_HOST },
-	{ "issn",         "ISSN",      SIMPLE, LEVEL_HOST },
-	{ "abstract",     "ABSTRACT",  SIMPLE, LEVEL_MAIN },
-	{ "contents",     "CONTENTS",  SIMPLE, LEVEL_MAIN },
-	{ "language",     "LANGUAGE",  SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",     BT_NOTE, LEVEL_MAIN },
-	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
-	{ "location",     "LOCATION",  SIMPLE, LEVEL_MAIN },
-	{ "ftp",          "",      BT_URL, LEVEL_MAIN },
-	{ "url",          "",      BT_URL, LEVEL_MAIN },
-	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
-	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
-	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
-	{ "howpublished", "",       HOWPUBLISHED, LEVEL_MAIN },
-	{ "doi",          "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "key",          "BIBKEY",       SIMPLE, LEVEL_MAIN },
-	{ "refnum",       "REFNUM",    SIMPLE, LEVEL_MAIN },
-	{ "crossref",     "CROSSREF",  SIMPLE, LEVEL_MAIN },
-	{ "keywords",     "KEYWORD",   KEYWORD, LEVEL_MAIN },
-	{ "",             "INTERNAL_TYPE|ARTICLE",           ALWAYS, LEVEL_MAIN },
+	{ "author",       "AUTHOR",         PERSON,       LEVEL_MAIN },
+	{ "translator",   "TRANSLATOR",     PERSON,       LEVEL_MAIN },
+	{ "title",        "",               TITLE,        LEVEL_MAIN },
+	{ "year",         "PARTDATE:YEAR",  SIMPLE,       LEVEL_MAIN },
+	{ "month",        "PARTDATE:MONTH", SIMPLE,       LEVEL_MAIN },
+	{ "day",          "PARTDATE:DAY",   SIMPLE,       LEVEL_MAIN },
+	{ "volume",       "VOLUME",         SIMPLE,       LEVEL_MAIN },
+	{ "pages",        "PAGES",          PAGES,        LEVEL_MAIN },
+	{ "number",       "ISSUE",          SIMPLE,       LEVEL_MAIN },
+	{ "issue",        "ISSUE",          SIMPLE,       LEVEL_MAIN },
+	{ "journal",      "",               TITLE,        LEVEL_HOST },
+	{ "publisher",    "PUBLISHER",      SIMPLE,       LEVEL_HOST },
+	{ "address",      "ADDRESS",        SIMPLE,       LEVEL_HOST },
+	{ "issn",         "ISSN",           SIMPLE,       LEVEL_HOST },
+	{ "coden",        "CODEN",          SIMPLE,       LEVEL_HOST },
+	{ "abstract",     "ABSTRACT",       SIMPLE,       LEVEL_MAIN },
+	{ "contents",     "CONTENTS",       SIMPLE,       LEVEL_MAIN },
+	{ "language",     "LANGUAGE",       SIMPLE,       LEVEL_MAIN },
+	{ "note",         "NOTES",          NOTES,        LEVEL_MAIN },
+	{ "annote",       "ANNOTE",         SIMPLE,       LEVEL_MAIN },
+	{ "location",     "LOCATION",       SIMPLE,       LEVEL_MAIN },
+	{ "ftp",          "",               URL,          LEVEL_MAIN },
+	{ "url",          "",               URL,          LEVEL_MAIN },
+	{ "pdf",          "FILEATTACH",     SIMPLE,       LEVEL_MAIN },
+	{ "eprint",       "",               EPRINT,       LEVEL_MAIN },
+	{ "archivePrefix","ARCHIVEPREFIX",  SIMPLE,       LEVEL_MAIN },
+	{ "eprintclass",  "EPRINTCLASS",    SIMPLE,       LEVEL_MAIN },
+	{ "primaryClass", "EPRINTCLASS",    SIMPLE,       LEVEL_MAIN },
+	{ "sentelink",    "FILEATTACH",     BT_SENTE,     LEVEL_MAIN },
+	{ "file",         "FILEATTACH",     LINKEDFILE,   LEVEL_MAIN },
+	{ "howpublished", "",               HOWPUBLISHED, LEVEL_MAIN },
+	{ "mrnumber",     "MRNUMBER",       SIMPLE,       LEVEL_MAIN },
+	{ "doi",          "DOI",            SIMPLE,       LEVEL_MAIN },
+	{ "key",          "BIBKEY",         SIMPLE,       LEVEL_MAIN },
+	{ "refnum",       "REFNUM",         SIMPLE,       LEVEL_MAIN },
+	{ "crossref",     "CROSSREF",       SIMPLE,       LEVEL_MAIN },
+	{ "keywords",     "KEYWORD",        KEYWORD,      LEVEL_MAIN },
+	{ "",             "INTERNAL_TYPE|ARTICLE",   ALWAYS, LEVEL_MAIN },
 	{ "",             "ISSUANCE|continuing",     ALWAYS, LEVEL_HOST },
 	{ "",             "RESOURCE|text",           ALWAYS, LEVEL_MAIN },
 	{ "",             "NGENRE|journal article",  ALWAYS, LEVEL_MAIN },
@@ -58,43 +64,43 @@
 /* Book */
 
 static lookups book[] = {
-	{ "author",       "AUTHOR",    PERSON, LEVEL_MAIN },
-	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "title",        "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "booktitle",    "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "series",       "TITLE",     TITLE,  LEVEL_HOST },
-	{ "publisher",    "PUBLISHER", SIMPLE, LEVEL_MAIN },
-	{ "organization", "ORGANIZER:CORP", BT_ORG, LEVEL_MAIN },
-	{ "address",      "ADDRESS",   SIMPLE, LEVEL_MAIN },
-	{ "editor",       "EDITOR",    PERSON, LEVEL_MAIN },
-	{ "year",         "YEAR",      SIMPLE, LEVEL_MAIN },
-	{ "month",        "MONTH",     SIMPLE, LEVEL_MAIN },
-	{ "day",          "DAY",       SIMPLE, LEVEL_MAIN },
-	{ "volume",       "VOLUME",    SIMPLE, LEVEL_MAIN },
-	{ "number",       "NUMBER",    SIMPLE, LEVEL_MAIN },
-	{ "isbn",         "ISBN",      SIMPLE, LEVEL_MAIN },
-	{ "lccn",         "LCCN",      SIMPLE, LEVEL_MAIN },
-	{ "edition",      "EDITION",   SIMPLE, LEVEL_MAIN },
-	{ "abstract",     "ABSTRACT",  SIMPLE, LEVEL_MAIN },
-	{ "contents",     "CONTENTS",  SIMPLE, LEVEL_MAIN },
-	{ "language",     "LANGUAGE",  SIMPLE, LEVEL_MAIN },
-	{ "location",     "LOCATION",  SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",     BT_NOTE, LEVEL_MAIN },
-	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
-	{ "type",         "GENRE",     SIMPLE, LEVEL_MAIN },
-	{ "key",          "BIBKEY",    SIMPLE, LEVEL_MAIN },
-	{ "ftp",          "",       BT_URL, LEVEL_MAIN },
-	{ "url",          "",       BT_URL, LEVEL_MAIN },
-	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
-	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
-	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
-	{ "address",      "ADDRESS",   SIMPLE, LEVEL_MAIN },
-	{ "howpublished", "",       HOWPUBLISHED, LEVEL_MAIN },
-	{ "doi",          "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "refnum",       "REFNUM",    SIMPLE, LEVEL_MAIN },
-	{ "crossref",     "CROSSREF",  SIMPLE, LEVEL_MAIN },
-	{ "keywords",     "KEYWORD",   KEYWORD, LEVEL_MAIN },
-	{ "refnum",       "REFNUM",    SIMPLE, LEVEL_MAIN },
+	{ "author",       "AUTHOR",         PERSON,       LEVEL_MAIN },
+	{ "translator",   "TRANSLATOR",     PERSON,       LEVEL_MAIN },
+	{ "title",        "",               TITLE,        LEVEL_MAIN },
+	{ "booktitle",    "",               TITLE,        LEVEL_MAIN },
+	{ "series",       "",               TITLE,        LEVEL_HOST },
+	{ "publisher",    "PUBLISHER",      SIMPLE,       LEVEL_MAIN },
+	{ "organization", "ORGANIZER:CORP", BT_ORG,       LEVEL_MAIN },
+	{ "address",      "ADDRESS",        SIMPLE,       LEVEL_MAIN },
+	{ "editor",       "EDITOR",         PERSON,       LEVEL_MAIN },
+	{ "year",         "DATE:YEAR",      SIMPLE,       LEVEL_MAIN },
+	{ "month",        "DATE:MONTH",     SIMPLE,       LEVEL_MAIN },
+	{ "day",          "DATE:DAY",       SIMPLE,       LEVEL_MAIN },
+	{ "volume",       "VOLUME",         SIMPLE,       LEVEL_MAIN },
+	{ "number",       "NUMBER",         SIMPLE,       LEVEL_MAIN },
+	{ "isbn",         "ISBN",           SIMPLE,       LEVEL_MAIN },
+	{ "lccn",         "LCCN",           SIMPLE,       LEVEL_MAIN },
+	{ "edition",      "EDITION",        SIMPLE,       LEVEL_MAIN },
+	{ "abstract",     "ABSTRACT",       SIMPLE,       LEVEL_MAIN },
+	{ "contents",     "CONTENTS",       SIMPLE,       LEVEL_MAIN },
+	{ "language",     "LANGUAGE",       SIMPLE,       LEVEL_MAIN },
+	{ "location",     "LOCATION",       SIMPLE,       LEVEL_MAIN },
+	{ "note",         "NOTES",          NOTES,        LEVEL_MAIN },
+	{ "annote",       "ANNOTE",         SIMPLE,       LEVEL_MAIN },
+	{ "type",         "GENRE",          SIMPLE,       LEVEL_MAIN },
+	{ "key",          "BIBKEY",         SIMPLE,       LEVEL_MAIN },
+	{ "ftp",          "",               URL,          LEVEL_MAIN },
+	{ "url",          "",               URL,          LEVEL_MAIN },
+	{ "pdf",          "FILEATTACH",     SIMPLE,       LEVEL_MAIN },
+	{ "sentelink",    "FILEATTACH",     BT_SENTE,     LEVEL_MAIN },
+	{ "file",         "FILEATTACH",     LINKEDFILE,   LEVEL_MAIN },
+	{ "address",      "ADDRESS",        SIMPLE,       LEVEL_MAIN },
+	{ "howpublished", "",               HOWPUBLISHED, LEVEL_MAIN },
+	{ "doi",          "DOI",            SIMPLE,       LEVEL_MAIN },
+	{ "refnum",       "REFNUM",         SIMPLE,       LEVEL_MAIN },
+	{ "crossref",     "CROSSREF",       SIMPLE,       LEVEL_MAIN },
+	{ "keywords",     "KEYWORD",        KEYWORD,      LEVEL_MAIN },
+	{ "refnum",       "REFNUM",         SIMPLE,       LEVEL_MAIN },
 	{ "",             "INTERNAL_TYPE|BOOK",   ALWAYS, LEVEL_MAIN },
 	{ "",             "RESOURCE|text",        ALWAYS, LEVEL_MAIN },
 	{ "",             "ISSUANCE|monographic", ALWAYS, LEVEL_MAIN },
@@ -104,39 +110,40 @@
 /* Technical reports */
 
 static lookups report[] = {
-	{ "author",       "AUTHOR",       PERSON, LEVEL_MAIN },
-	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "title",        "TITLE",        TITLE,  LEVEL_MAIN },
-	{ "booktitle",    "TITLE",        TITLE,  LEVEL_MAIN },
-	{ "series",       "TITLE",        TITLE,  LEVEL_HOST },
-	{ "editor",       "EDITOR",       PERSON, LEVEL_MAIN },
-	{ "publisher",    "PUBLISHER",    SIMPLE, LEVEL_MAIN },
-	{ "institution",  "PUBLISHER",    SIMPLE, LEVEL_MAIN },
-	{ "address",      "ADDRESS",      SIMPLE, LEVEL_MAIN },
-	{ "year",         "YEAR",         SIMPLE, LEVEL_MAIN },
-	{ "month",        "MONTH",        SIMPLE, LEVEL_MAIN },
-	{ "day",          "DAY",          SIMPLE, LEVEL_MAIN },
-	{ "number",       "NUMBER",       SIMPLE, LEVEL_MAIN },
-	{ "isbn",         "ISBN",         SIMPLE, LEVEL_MAIN },
-	{ "lccn",         "LCCN",         SIMPLE, LEVEL_MAIN },
-	{ "abstract",     "ABSTRACT",     SIMPLE, LEVEL_MAIN },
-	{ "contents",     "CONTENTS",     SIMPLE, LEVEL_MAIN },
-	{ "language",     "LANGUAGE",     SIMPLE, LEVEL_MAIN },
-	{ "location",     "LOCATION",     SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",        BT_NOTE, LEVEL_MAIN },
-	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
-	{ "key",          "BIBKEY",          SIMPLE, LEVEL_MAIN },
-	{ "doi",          "DOI",          SIMPLE, LEVEL_MAIN },
-	{ "ftp",          "",          BT_URL, LEVEL_MAIN },
-	{ "url",          "",          BT_URL, LEVEL_MAIN },
-	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
-	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
-	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
-	{ "howpublished", "",          HOWPUBLISHED, LEVEL_MAIN },
-	{ "refnum",    "REFNUM",          SIMPLE, LEVEL_MAIN },
-	{ "crossref",     "CROSSREF",  SIMPLE, LEVEL_MAIN },
-	{ "type",      "GENRE",            SIMPLE, LEVEL_MAIN },
-	{ "keywords",     "KEYWORD",   KEYWORD, LEVEL_MAIN },
+	{ "author",       "AUTHOR",     PERSON,       LEVEL_MAIN },
+	{ "translator",   "TRANSLATOR", PERSON,       LEVEL_MAIN },
+	{ "title",        "",           TITLE,        LEVEL_MAIN },
+	{ "booktitle",    "",           TITLE,        LEVEL_MAIN },
+	{ "series",       "",           TITLE,        LEVEL_HOST },
+	{ "editor",       "EDITOR",     PERSON,       LEVEL_MAIN },
+	{ "publisher",    "PUBLISHER",  SIMPLE,       LEVEL_MAIN },
+	{ "institution",  "PUBLISHER",  SIMPLE,       LEVEL_MAIN },
+	{ "address",      "ADDRESS",    SIMPLE,       LEVEL_MAIN },
+	{ "year",         "DATE:YEAR",  SIMPLE,       LEVEL_MAIN },
+	{ "month",        "DATE:MONTH", SIMPLE,       LEVEL_MAIN },
+	{ "day",          "DATE:DAY",   SIMPLE,       LEVEL_MAIN },
+	{ "number",       "NUMBER",     SIMPLE,       LEVEL_MAIN },
+	{ "isbn",         "ISBN",       SIMPLE,       LEVEL_MAIN },
+	{ "lccn",         "LCCN",       SIMPLE,       LEVEL_MAIN },
+	{ "abstract",     "ABSTRACT",   SIMPLE,       LEVEL_MAIN },
+	{ "contents",     "CONTENTS",   SIMPLE,       LEVEL_MAIN },
+	{ "language",     "LANGUAGE",   SIMPLE,       LEVEL_MAIN },
+	{ "location",     "LOCATION",   SIMPLE,       LEVEL_MAIN },
+	{ "note",         "NOTES",      NOTES,        LEVEL_MAIN },
+	{ "annote",       "ANNOTE",     SIMPLE,       LEVEL_MAIN },
+	{ "key",          "BIBKEY",     SIMPLE,       LEVEL_MAIN },
+	{ "doi",          "DOI",        SIMPLE,       LEVEL_MAIN },
+	{ "ftp",          "",           URL,          LEVEL_MAIN },
+	{ "url",          "",           URL,          LEVEL_MAIN },
+	{ "mrnumber",     "MRNUMBER",   SIMPLE,       LEVEL_MAIN },
+	{ "pdf",          "FILEATTACH", SIMPLE,       LEVEL_MAIN },
+	{ "sentelink",    "FILEATTACH", BT_SENTE,     LEVEL_MAIN },
+	{ "file",         "FILEATTACH", LINKEDFILE,   LEVEL_MAIN },
+	{ "howpublished", "",           HOWPUBLISHED, LEVEL_MAIN },
+	{ "refnum",       "REFNUM",     SIMPLE,       LEVEL_MAIN },
+	{ "crossref",     "CROSSREF",   SIMPLE,       LEVEL_MAIN },
+	{ "type",         "GENRE",      SIMPLE,       LEVEL_MAIN },
+	{ "keywords",     "KEYWORD",    KEYWORD,      LEVEL_MAIN },
 	{ "",         "INTERNAL_TYPE|REPORT", ALWAYS, LEVEL_MAIN },
 	{ "",         "RESOURCE|text",        ALWAYS, LEVEL_MAIN },
 	{ "",         "GENRE|report",         ALWAYS, LEVEL_MAIN }
@@ -145,27 +152,27 @@
 static lookups manual[] = {
 	{ "author",    "AUTHOR",    PERSON, LEVEL_MAIN },
 	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "title",     "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "booktitle", "TITLE",     TITLE,  LEVEL_MAIN },
+	{ "title",     "",     TITLE,  LEVEL_MAIN },
+	{ "booktitle", "",     TITLE,  LEVEL_MAIN },
 	{ "editor",    "EDITOR",    PERSON, LEVEL_MAIN },
 	{ "publisher", "PUBLISHER", SIMPLE, LEVEL_MAIN },
 	{ "organization", "ORGANIZER:CORP", BT_ORG, LEVEL_MAIN },
 	{ "address",   "ADDRESS",   SIMPLE, LEVEL_MAIN },
-	{ "year",      "YEAR",      SIMPLE, LEVEL_MAIN },
-	{ "month",     "MONTH",     SIMPLE, LEVEL_MAIN },
-	{ "day",       "DAY",       SIMPLE, LEVEL_MAIN },
+	{ "year",      "DATE:YEAR",      SIMPLE, LEVEL_MAIN },
+	{ "month",     "DATE:MONTH",     SIMPLE, LEVEL_MAIN },
+	{ "day",       "DATE:DAY",       SIMPLE, LEVEL_MAIN },
 	{ "isbn",      "ISBN",      SIMPLE, LEVEL_MAIN },
 	{ "lccn",      "LCCN",      SIMPLE, LEVEL_MAIN },
 	{ "edition",   "EDITION",   SIMPLE, LEVEL_MAIN },
 	{ "abstract",  "ABSTRACT",  SIMPLE, LEVEL_MAIN },
 	{ "contents",  "CONTENTS",  SIMPLE, LEVEL_MAIN },
 	{ "location",     "LOCATION",     SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",        BT_NOTE, LEVEL_MAIN },
+	{ "note",         "NOTES",        NOTES,   LEVEL_MAIN },
 	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
 	{ "key",          "BIBKEY",          SIMPLE, LEVEL_MAIN },
 	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",       BT_URL, LEVEL_MAIN },
-	{ "url",       "",       BT_URL, LEVEL_MAIN },
+	{ "ftp",       "",       URL,    LEVEL_MAIN },
+	{ "url",       "",       URL,    LEVEL_MAIN },
 	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
 	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
 	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
@@ -182,65 +189,65 @@
 /* Part of a book (e.g. chapter or section) */
 
 static lookups inbook[] = {
-	{ "author",    "AUTHOR",    PERSON, LEVEL_MAIN },
+	{ "author",       "AUTHOR",    PERSON, LEVEL_MAIN },
 	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "editor",    "EDITOR",    PERSON, LEVEL_HOST },
-	{ "chapter",   "CHAPTER",   SIMPLE, LEVEL_MAIN },
-	{ "title",     "TITLE",     TITLE,  LEVEL_HOST },
-	{ "booktitle", "TITLE",     TITLE,  LEVEL_HOST },
-	{ "series",    "TITLE",     TITLE,  LEVEL_SERIES },
-	{ "publisher", "PUBLISHER", SIMPLE, LEVEL_HOST },
-	{ "address",   "ADDRESS",   SIMPLE, LEVEL_HOST },
-	{ "year",      "YEAR",      SIMPLE, LEVEL_HOST },
-	{ "month",     "MONTH",     SIMPLE, LEVEL_HOST },
-	{ "day",       "DAY",       SIMPLE, LEVEL_HOST },
-	{ "volume",    "VOLUME",    SIMPLE, LEVEL_SERIES },
-	{ "number",    "NUMBER",    SIMPLE, LEVEL_SERIES },
-	{ "pages",     "PAGES",     PAGES,  LEVEL_HOST },
-	{ "isbn",      "ISBN",      SIMPLE, LEVEL_HOST },
-	{ "lccn",      "LCCN",      SIMPLE, LEVEL_HOST },
-	{ "edition",   "EDITION",   SIMPLE, LEVEL_HOST },
-	{ "abstract",  "ABSTRACT",  SIMPLE, LEVEL_MAIN },
-	{ "contents",  "CONTENTS",  SIMPLE, LEVEL_HOST },
-	{ "language",     "LANGUAGE",     SIMPLE, LEVEL_MAIN },
-	{ "location",     "LOCATION",     SIMPLE, LEVEL_HOST },
-	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",       BT_URL, LEVEL_MAIN },
-	{ "url",       "",       BT_URL, LEVEL_MAIN },
+	{ "editor",       "EDITOR",    PERSON, LEVEL_HOST },
+	{ "chapter",      "CHAPTER",   SIMPLE, LEVEL_MAIN },   /* chapter number */
+	{ "title",        "",          TITLE,  LEVEL_MAIN },   /* chapter name */
+	{ "booktitle",    "",          TITLE,  LEVEL_HOST },
+	{ "series",       "",          TITLE,  LEVEL_SERIES },
+	{ "publisher",    "PUBLISHER", SIMPLE, LEVEL_HOST },
+	{ "address",      "ADDRESS",   SIMPLE, LEVEL_HOST },
+	{ "year",         "DATE:YEAR", SIMPLE, LEVEL_HOST },
+	{ "month",        "DATE:MONTH",SIMPLE, LEVEL_HOST },
+	{ "day",          "DATE:DAY",  SIMPLE, LEVEL_HOST },
+	{ "volume",       "VOLUME",    SIMPLE, LEVEL_SERIES },
+	{ "number",       "NUMBER",    SIMPLE, LEVEL_SERIES },
+	{ "pages",        "PAGES",     PAGES,  LEVEL_HOST },
+	{ "isbn",         "ISBN",      SIMPLE, LEVEL_HOST },
+	{ "lccn",         "LCCN",      SIMPLE, LEVEL_HOST },
+	{ "edition",      "EDITION",   SIMPLE, LEVEL_HOST },
+	{ "abstract",     "ABSTRACT",  SIMPLE, LEVEL_MAIN },
+	{ "contents",     "CONTENTS",  SIMPLE, LEVEL_HOST },
+	{ "language",     "LANGUAGE",  SIMPLE, LEVEL_MAIN },
+	{ "location",     "LOCATION",  SIMPLE, LEVEL_HOST },
+	{ "doi",          "DOI",       SIMPLE, LEVEL_MAIN },
+	{ "ftp",          "",          URL,    LEVEL_MAIN },
+	{ "url",          "",          URL,    LEVEL_MAIN },
 	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
 	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
 	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
 	{ "howpublished", "",    HOWPUBLISHED, LEVEL_MAIN },
-	{ "refnum",    "REFNUM",    SIMPLE, LEVEL_MAIN },
+	{ "refnum",       "REFNUM",    SIMPLE, LEVEL_MAIN },
 	{ "crossref",     "CROSSREF",  SIMPLE, LEVEL_MAIN },
-	{ "type",      "GENRE",      SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",        BT_NOTE, LEVEL_MAIN },
+	{ "type",         "GENRE",      SIMPLE, LEVEL_MAIN },
+	{ "note",         "NOTES",        NOTES,   LEVEL_MAIN },
 	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
 	{ "key",          "BIBKEY",          SIMPLE, LEVEL_MAIN },
 	{ "keywords",     "KEYWORD",   KEYWORD, LEVEL_MAIN },
-	{ "",         "INTERNAL_TYPE|INBOOK", ALWAYS, LEVEL_MAIN },
-	{ "",         "RESOURCE|text",        ALWAYS, LEVEL_MAIN },
-	{ "",         "ISSUANCE|monographic", ALWAYS, LEVEL_HOST },
-	{ "",         "NGENRE|book chapter",  ALWAYS, LEVEL_MAIN },
-	{ "",         "GENRE|book",           ALWAYS, LEVEL_HOST }
+	{ "",             "INTERNAL_TYPE|INBOOK", ALWAYS, LEVEL_MAIN },
+	{ "",             "RESOURCE|text",        ALWAYS, LEVEL_MAIN },
+	{ "",             "ISSUANCE|monographic", ALWAYS, LEVEL_HOST },
+	{ "",             "NGENRE|book chapter",  ALWAYS, LEVEL_MAIN },
+	{ "",             "GENRE|book",           ALWAYS, LEVEL_HOST }
 };
 
 /* References of papers in conference proceedings */
 
 static lookups inproceedings[] = {
-	{ "author",    "AUTHOR",    PERSON, LEVEL_MAIN },
-	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "editor",    "EDITOR",    PERSON, LEVEL_HOST },
-	{ "title",     "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "booktitle", "TITLE",     TITLE,  LEVEL_HOST },
-	{ "series",    "TITLE",     TITLE,  LEVEL_SERIES },
-	{ "chapter",   "CHAPTER",   SIMPLE, LEVEL_MAIN },
-	{ "publisher", "PUBLISHER", SIMPLE, LEVEL_HOST },
+	{ "author",     "AUTHOR",     PERSON, LEVEL_MAIN },
+	{ "translator", "TRANSLATOR", PERSON, LEVEL_MAIN },
+	{ "editor",     "EDITOR",     PERSON, LEVEL_HOST },
+	{ "chapter",    "CHAPTER",    SIMPLE, LEVEL_MAIN },   /* chapter number */
+	{ "title",      "",           TITLE,  LEVEL_MAIN },   /* chapter name */
+	{ "booktitle",  "",           TITLE,  LEVEL_HOST },
+	{ "series",     "",           TITLE,  LEVEL_SERIES },
+	{ "publisher",  "PUBLISHER",  SIMPLE, LEVEL_HOST },
 	{ "organization", "ORGANIZER:CORP", BT_ORG, LEVEL_HOST },
 	{ "address",   "ADDRESS",   SIMPLE, LEVEL_HOST },
-	{ "year",      "PARTYEAR",  SIMPLE, LEVEL_MAIN },
-	{ "month",     "PARTMONTH", SIMPLE, LEVEL_MAIN },
-	{ "day",       "PARTDAY",   SIMPLE, LEVEL_MAIN },
+	{ "year",      "PARTDATE:YEAR",  SIMPLE, LEVEL_MAIN },
+	{ "month",     "PARTDATE:MONTH", SIMPLE, LEVEL_MAIN },
+	{ "day",       "PARTDATE:DAY",   SIMPLE, LEVEL_MAIN },
 	{ "volume",    "VOLUME",    SIMPLE, LEVEL_MAIN },
 	{ "number",    "NUMBER",    SIMPLE, LEVEL_MAIN },
 	{ "pages",     "PAGES",     PAGES,  LEVEL_MAIN },
@@ -252,13 +259,13 @@
 	{ "paper",     "PAPER",     SIMPLE, LEVEL_MAIN },
 	{ "location",     "LOCATION",     SIMPLE, LEVEL_HOST },
 	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",       BT_URL, LEVEL_MAIN },
-	{ "url",       "",       BT_URL, LEVEL_MAIN },
+	{ "ftp",       "",          URL,    LEVEL_MAIN },
+	{ "url",       "",          URL,    LEVEL_MAIN },
 	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
 	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
 	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
 	{ "type",      "GENRE",      SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",        BT_NOTE, LEVEL_MAIN },
+	{ "note",         "NOTES",        NOTES,   LEVEL_MAIN },
 	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
 	{ "key",          "BIBKEY",          SIMPLE, LEVEL_MAIN },
 	{ "howpublished", "",    HOWPUBLISHED, LEVEL_MAIN },
@@ -273,18 +280,18 @@
 /* Used for a section of a book with its own title */
 
 static lookups incollection[] = {
-	{ "author",    "AUTHOR",    PERSON, LEVEL_MAIN },
-	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "editor",    "EDITOR",    PERSON, LEVEL_HOST },
-	{ "title",     "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "chapter",   "CHAPTER",   SIMPLE, LEVEL_MAIN },
-	{ "booktitle", "TITLE",     TITLE,  LEVEL_HOST },
-	{ "series",    "TITLE",     TITLE,  LEVEL_SERIES },
-	{ "publisher", "PUBLISHER", SIMPLE, LEVEL_HOST },
+	{ "author",     "AUTHOR",     PERSON, LEVEL_MAIN },
+	{ "translator", "TRANSLATOR", PERSON, LEVEL_MAIN },
+	{ "editor",     "EDITOR",     PERSON, LEVEL_HOST },
+	{ "chapter",    "CHAPTER",    SIMPLE, LEVEL_MAIN },   /* chapter number */
+	{ "title",      "",           TITLE,  LEVEL_MAIN },   /* chapter name */
+	{ "booktitle",  "",           TITLE,  LEVEL_HOST },
+	{ "series",     "",           TITLE,  LEVEL_SERIES },
+	{ "publisher",  "PUBLISHER",  SIMPLE, LEVEL_HOST },
 	{ "address",   "ADDRESS",   SIMPLE, LEVEL_HOST },
-	{ "year",      "YEAR",      SIMPLE, LEVEL_HOST },
-	{ "month",     "MONTH",     SIMPLE, LEVEL_HOST },
-	{ "day",       "DAY",       SIMPLE, LEVEL_HOST },
+	{ "year",      "DATE:YEAR",      SIMPLE, LEVEL_HOST },
+	{ "month",     "DATE:MONTH",     SIMPLE, LEVEL_HOST },
+	{ "day",       "DATE:DAY",       SIMPLE, LEVEL_HOST },
 	{ "volume",    "VOLUME",    SIMPLE, LEVEL_MAIN },
 	{ "number",    "NUMBER",    SIMPLE, LEVEL_MAIN },
 	{ "pages",     "PAGES",     PAGES,  LEVEL_MAIN },
@@ -293,17 +300,17 @@
 	{ "edition",   "EDITION",   SIMPLE, LEVEL_HOST },
 	{ "abstract",  "ABSTRACT",  SIMPLE, LEVEL_MAIN },
 	{ "contents",  "CONTENTS",  SIMPLE, LEVEL_HOST },
-	{ "language",     "LANGUAGE",     SIMPLE, LEVEL_MAIN },
-	{ "type",      "GENRE",      SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",        BT_NOTE, LEVEL_MAIN },
-	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
-	{ "key",          "BIBKEY",          SIMPLE, LEVEL_MAIN },
+	{ "language",  "LANGUAGE",  SIMPLE, LEVEL_MAIN },
+	{ "type",      "GENRE",     SIMPLE, LEVEL_MAIN },
+	{ "note",      "NOTES",     NOTES,  LEVEL_MAIN },
+	{ "annote",    "ANNOTE",    SIMPLE, LEVEL_MAIN },
+	{ "key",       "BIBKEY",    SIMPLE, LEVEL_MAIN },
 	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",       BT_URL, LEVEL_MAIN },
-	{ "url",       "",       BT_URL, LEVEL_MAIN },
-	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
-	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
-	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
+	{ "ftp",       "",          URL,    LEVEL_MAIN },
+	{ "url",       "",          URL,    LEVEL_MAIN },
+	{ "pdf",       "FILEATTACH",SIMPLE, LEVEL_MAIN },
+	{ "sentelink", "FILEATTACH",BT_SENTE, LEVEL_MAIN },
+	{ "file",      "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
 	{ "location",     "LOCATION",     SIMPLE, LEVEL_HOST },
 	{ "howpublished", "",    HOWPUBLISHED, LEVEL_MAIN },
 	{ "refnum",    "REFNUM",    SIMPLE, LEVEL_MAIN },
@@ -321,15 +328,15 @@
 	{ "author",    "AUTHOR",    PERSON, LEVEL_MAIN },
 	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "editor",    "EDITOR",    PERSON, LEVEL_MAIN },
-	{ "title",     "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "booktitle", "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "series",    "TITLE",     TITLE,  LEVEL_HOST },
+	{ "title",     "",     TITLE,  LEVEL_MAIN },
+	{ "booktitle", "",     TITLE,  LEVEL_MAIN },
+	{ "series",    "",     TITLE,  LEVEL_HOST },
 	{ "publisher", "PUBLISHER", SIMPLE, LEVEL_MAIN },
 	{ "organization", "ORGANIZER:CORP", BT_ORG, LEVEL_MAIN },
 	{ "address",   "ADDRESS",   SIMPLE, LEVEL_MAIN },
-	{ "year",      "YEAR",      SIMPLE, LEVEL_MAIN },
-	{ "month",     "MONTH",     SIMPLE, LEVEL_MAIN },
-	{ "day",       "DAY",       SIMPLE, LEVEL_MAIN },
+	{ "year",      "DATE:YEAR",      SIMPLE, LEVEL_MAIN },
+	{ "month",     "DATE:MONTH",     SIMPLE, LEVEL_MAIN },
+	{ "day",       "DATE:DAY",       SIMPLE, LEVEL_MAIN },
 	{ "volume",    "VOLUME",    SIMPLE, LEVEL_MAIN },
 	{ "number",    "NUMBER",    SIMPLE, LEVEL_MAIN },
 	{ "pages",     "PAGES",     PAGES,  LEVEL_MAIN },
@@ -338,15 +345,15 @@
 	{ "abstract",  "ABSTRACT",  SIMPLE, LEVEL_MAIN },
 	{ "contents",  "CONTENTS",  SIMPLE, LEVEL_MAIN },
 	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",       BT_URL, LEVEL_MAIN },
-	{ "url",       "",       BT_URL, LEVEL_MAIN },
+	{ "ftp",       "",          URL,    LEVEL_MAIN },
+	{ "url",       "",          URL,    LEVEL_MAIN },
 	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
 	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
 	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
 	{ "language",     "LANGUAGE",     SIMPLE, LEVEL_MAIN },
 	{ "howpublished", "",    HOWPUBLISHED, LEVEL_MAIN },
 	{ "location",     "LOCATION",     SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",        BT_NOTE, LEVEL_MAIN },
+	{ "note",         "NOTES",        NOTES,   LEVEL_MAIN },
 	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
 	{ "key",          "BIBKEY",          SIMPLE, LEVEL_MAIN },
 	{ "refnum",    "REFNUM",    SIMPLE, LEVEL_MAIN },
@@ -360,24 +367,24 @@
 static lookups phds[] = {
 	{ "author",    "AUTHOR",    PERSON, LEVEL_MAIN },
 	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "title",     "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "year",      "YEAR",      SIMPLE, LEVEL_MAIN },
-	{ "month",     "MONTH",     SIMPLE, LEVEL_MAIN },
-	{ "day",       "DAY",       SIMPLE, LEVEL_MAIN },
+	{ "title",     "",     TITLE,  LEVEL_MAIN },
+	{ "year",      "DATE:YEAR",      SIMPLE, LEVEL_MAIN },
+	{ "month",     "DATE:MONTH",     SIMPLE, LEVEL_MAIN },
+	{ "day",       "DATE:DAY",       SIMPLE, LEVEL_MAIN },
 	{ "school",    "DEGREEGRANTOR:ASIS",SIMPLE, LEVEL_MAIN },
 	{ "address",   "ADDRESS",   SIMPLE, LEVEL_MAIN },
 	{ "abstract",  "ABSTRACT",  SIMPLE, LEVEL_MAIN },
 	{ "contents",  "CONTENTS",  SIMPLE, LEVEL_MAIN },
 	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",       BT_URL, LEVEL_MAIN },
-	{ "url",       "",       BT_URL, LEVEL_MAIN },
+	{ "ftp",       "",          URL,    LEVEL_MAIN },
+	{ "url",       "",          URL,    LEVEL_MAIN },
 	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
 	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
 	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
 	{ "howpublished", "" ,   HOWPUBLISHED, LEVEL_MAIN },
 	{ "language",     "LANGUAGE",     SIMPLE, LEVEL_MAIN },
 	{ "location",     "LOCATION",     SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",        BT_NOTE, LEVEL_MAIN },
+	{ "note",         "NOTES",        NOTES,   LEVEL_MAIN },
 	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
 	{ "key",          "BIBKEY",          SIMPLE, LEVEL_MAIN },
 	{ "type",         "GENRE",         SIMPLE, LEVEL_MAIN },
@@ -393,23 +400,23 @@
 static lookups masters[] = {
 	{ "author",    "AUTHOR",    PERSON, LEVEL_MAIN },
 	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "title",     "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "year",      "YEAR",      SIMPLE, LEVEL_MAIN },
-	{ "month",     "MONTH",     SIMPLE, LEVEL_MAIN },
-	{ "day",       "DAY",       SIMPLE, LEVEL_MAIN },
+	{ "title",     "",     TITLE,  LEVEL_MAIN },
+	{ "year",      "DATE:YEAR",      SIMPLE, LEVEL_MAIN },
+	{ "month",     "DATE:MONTH",     SIMPLE, LEVEL_MAIN },
+	{ "day",       "DATE:DAY",       SIMPLE, LEVEL_MAIN },
 	{ "school",    "DEGREEGRANTOR:ASIS",SIMPLE, LEVEL_MAIN },
 	{ "address",   "ADDRESS",   SIMPLE, LEVEL_MAIN },
 	{ "abstract",  "ABSTRACT",  SIMPLE, LEVEL_MAIN },
 	{ "contents",  "CONTENTS",  SIMPLE, LEVEL_MAIN },
 	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",       BT_URL, LEVEL_MAIN },
-	{ "url",       "",       BT_URL, LEVEL_MAIN },
+	{ "ftp",       "",          URL,   LEVEL_MAIN },
+	{ "url",       "",          URL,   LEVEL_MAIN },
 	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
 	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
 	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
 	{ "language",     "LANGUAGE",     SIMPLE, LEVEL_MAIN },
 	{ "location",     "LOCATION",     SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",        BT_NOTE, LEVEL_MAIN },
+	{ "note",         "NOTES",        NOTES,   LEVEL_MAIN },
 	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
 	{ "key",          "BIBKEY",          SIMPLE, LEVEL_MAIN },
 	{ "type",         "GENRE",         SIMPLE, LEVEL_MAIN },
@@ -428,26 +435,26 @@
 static lookups unpublished[] = {
 	{ "author",    "AUTHOR",    PERSON, LEVEL_MAIN },
 	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "title",     "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "booktitle", "TITLE",     TITLE,  LEVEL_MAIN },
+	{ "title",     "",     TITLE,  LEVEL_MAIN },
+	{ "booktitle", "",     TITLE,  LEVEL_MAIN },
 	{ "publisher", "PUBLISHER", SIMPLE, LEVEL_MAIN },
 	{ "address",   "ADDRESS",   SIMPLE, LEVEL_MAIN },
 	{ "editor",    "EDITOR",    PERSON, LEVEL_MAIN },
-	{ "year",      "YEAR",      SIMPLE, LEVEL_MAIN },
-	{ "month",     "MONTH",     SIMPLE, LEVEL_MAIN },
-	{ "day",       "DAY",       SIMPLE, LEVEL_MAIN },
+	{ "year",      "DATE:YEAR",      SIMPLE, LEVEL_MAIN },
+	{ "month",     "DATE:MONTH",     SIMPLE, LEVEL_MAIN },
+	{ "day",       "DATE:DAY",       SIMPLE, LEVEL_MAIN },
 	{ "abstract",  "ABSTRACT",  SIMPLE, LEVEL_MAIN },
 	{ "contents",  "CONTENTS",  SIMPLE, LEVEL_MAIN },
 	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",       BT_URL, LEVEL_MAIN },
-	{ "url",       "",       BT_URL, LEVEL_MAIN },
+	{ "ftp",       "",          URL,    LEVEL_MAIN },
+	{ "url",       "",          URL,    LEVEL_MAIN },
 	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
 	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
 	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
 	{ "language",     "LANGUAGE",     SIMPLE, LEVEL_MAIN },
 	{ "howpublished", "",    HOWPUBLISHED, LEVEL_MAIN },
 	{ "location",     "LOCATION",     SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",        BT_NOTE, LEVEL_MAIN },
+	{ "note",         "NOTES",        NOTES,   LEVEL_MAIN },
 	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
 	{ "key",          "BIBKEY",          SIMPLE, LEVEL_MAIN },
 	{ "refnum",    "REFNUM",    SIMPLE, LEVEL_MAIN },
@@ -466,32 +473,32 @@
 	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "organization", "ORGANIZER:CORP", BT_ORG, LEVEL_MAIN },
 	{ "institution", "AUTHOR:CORP", SIMPLE, LEVEL_MAIN },
-	{ "title",     "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "booktitle", "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "publisher", "PUBLISHER", SIMPLE, LEVEL_MAIN },
-	{ "address",   "ADDRESS",   SIMPLE, LEVEL_MAIN },
-	{ "editor",    "EDITOR",    PERSON, LEVEL_MAIN },
-	{ "year",      "YEAR",      SIMPLE, LEVEL_MAIN },
-	{ "month",     "MONTH",     SIMPLE, LEVEL_MAIN },
-	{ "day",       "DAY",       SIMPLE, LEVEL_MAIN },
-	{ "isbn",      "ISBN",      SIMPLE, LEVEL_MAIN },
-	{ "lccn",      "LCCN",      SIMPLE, LEVEL_MAIN },
-	{ "abstract",  "ABSTRACT",  SIMPLE, LEVEL_MAIN },
-	{ "contents",  "CONTENTS",  SIMPLE, LEVEL_MAIN },
-	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",      BT_URL, LEVEL_MAIN },
-	{ "url",       "",      BT_URL, LEVEL_MAIN },
-	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
-	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
+	{ "title",     "",           TITLE,    LEVEL_MAIN },
+	{ "booktitle", "",           TITLE,    LEVEL_MAIN },
+	{ "publisher", "PUBLISHER",  SIMPLE,   LEVEL_MAIN },
+	{ "address",   "ADDRESS",    SIMPLE,   LEVEL_MAIN },
+	{ "editor",    "EDITOR",     PERSON,   LEVEL_MAIN },
+	{ "year",      "DATE:YEAR",  SIMPLE,   LEVEL_MAIN },
+	{ "month",     "DATE:MONTH", SIMPLE,   LEVEL_MAIN },
+	{ "day",       "DATE:DAY",   SIMPLE,   LEVEL_MAIN },
+	{ "isbn",      "ISBN",       SIMPLE,   LEVEL_MAIN },
+	{ "lccn",      "LCCN",       SIMPLE,   LEVEL_MAIN },
+	{ "abstract",  "ABSTRACT",   SIMPLE,   LEVEL_MAIN },
+	{ "contents",  "CONTENTS",   SIMPLE,   LEVEL_MAIN },
+	{ "doi",       "DOI",        SIMPLE,   LEVEL_MAIN },
+	{ "ftp",       "",           URL,      LEVEL_MAIN },
+	{ "url",       "",           URL,      LEVEL_MAIN },
+	{ "pdf",       "FILEATTACH", SIMPLE,   LEVEL_MAIN },
+	{ "sentelink", "FILEATTACH", BT_SENTE, LEVEL_MAIN },
 	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
 	{ "howpublished", "",    HOWPUBLISHED, LEVEL_MAIN },
 	{ "language",     "LANGUAGE",     SIMPLE, LEVEL_MAIN },
 	{ "refnum",    "REFNUM",    SIMPLE, LEVEL_MAIN },
 	{ "type",      "GENRE",      SIMPLE, LEVEL_MAIN },
-	{ "number",    "NUMBER",      TITLE,  LEVEL_MAIN },
-	{ "revision",  "REVISION",    TITLE,  LEVEL_MAIN },
+	{ "number",    "NUMBER",      SIMPLE,  LEVEL_MAIN },
+	{ "revision",  "EDITION",    SIMPLE,  LEVEL_MAIN },
 	{ "location",     "LOCATION",     SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",        BT_NOTE, LEVEL_MAIN },
+	{ "note",         "NOTES",        NOTES,   LEVEL_MAIN },
 	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
 	{ "key",          "BIBKEY",          SIMPLE, LEVEL_MAIN },
 	{ "keywords",     "KEYWORD",   KEYWORD, LEVEL_MAIN },
@@ -504,30 +511,31 @@
  * For Journals and Magazines
  */
 static lookups periodical[] = {
-	{ "title",        "TITLE",       TITLE,  LEVEL_MAIN },
-	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "series",       "TITLE",       TITLE,  LEVEL_HOST },
+	{ "title",        "",            TITLE,  LEVEL_MAIN },
+	{ "translator",   "TRANSLATOR",  PERSON, LEVEL_MAIN },
+	{ "series",       "",            TITLE,  LEVEL_HOST },
 	{ "publisher",    "PUBLISHER",   SIMPLE, LEVEL_MAIN },
 	{ "address",      "ADDRESS",     SIMPLE, LEVEL_MAIN },
 	{ "editor",       "EDITOR",      PERSON, LEVEL_MAIN },
-	{ "year",         "YEAR",        SIMPLE, LEVEL_MAIN },
-	{ "month",        "MONTH",       SIMPLE, LEVEL_MAIN },
-	{ "day",          "DAY",         SIMPLE, LEVEL_MAIN },
+	{ "year",         "DATE:YEAR",   SIMPLE, LEVEL_MAIN },
+	{ "month",        "DATE:MONTH",  SIMPLE, LEVEL_MAIN },
+	{ "day",          "DATE:DAY",    SIMPLE, LEVEL_MAIN },
 	{ "issn",         "ISSN",        SIMPLE, LEVEL_MAIN },
+	{ "coden",        "CODEN",       SIMPLE, LEVEL_MAIN },
 	{ "abstract",     "ABSTRACT",    SIMPLE, LEVEL_MAIN },
-	{ "volume",       "VOLUME",      TITLE,  LEVEL_MAIN },
-	{ "number",       "NUMBER",      TITLE,  LEVEL_MAIN },
+	{ "volume",       "VOLUME",      SIMPLE, LEVEL_MAIN },
+	{ "number",       "NUMBER",      SIMPLE, LEVEL_MAIN },
 	{ "contents",     "CONTENTS",    SIMPLE, LEVEL_MAIN },
 	{ "doi",          "DOI",         SIMPLE, LEVEL_MAIN },
-	{ "ftp",          "",        BT_URL, LEVEL_MAIN },
-	{ "url",          "",        BT_URL, LEVEL_MAIN },
-	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
-	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
-	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
-	{ "howpublished", "",         HOWPUBLISHED, LEVEL_MAIN },
+	{ "ftp",          "",            URL,    LEVEL_MAIN },
+	{ "url",          "",            URL,    LEVEL_MAIN },
+	{ "pdf",          "FILEATTACH",  SIMPLE, LEVEL_MAIN },
+	{ "sentelink",    "FILEATTACH",  BT_SENTE, LEVEL_MAIN },
+	{ "file",         "FILEATTACH",  LINKEDFILE,   LEVEL_MAIN },
+	{ "howpublished", "",            HOWPUBLISHED, LEVEL_MAIN },
 	{ "refnum",       "REFNUM",      SIMPLE, LEVEL_MAIN },
 	{ "language",     "LANGUAGE",    SIMPLE, LEVEL_MAIN },
-	{ "note",         "NOTES",       BT_NOTE, LEVEL_MAIN },
+	{ "note",         "NOTES",       NOTES,   LEVEL_MAIN },
 	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
 	{ "organization", "ORGANIZER:CORP", BT_ORG, LEVEL_MAIN },
 	{ "crossref",     "CROSSREF",  SIMPLE, LEVEL_MAIN },
@@ -540,38 +548,38 @@
  * For Patent
  */
 static lookups patent[] = {
-	{ "author",    "AUTHOR",    PERSON, LEVEL_MAIN },
-	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "assignee",  "ASSIGNEE",  PERSON, LEVEL_MAIN },
-	{ "title",     "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "publisher", "PUBLISHER", SIMPLE, LEVEL_MAIN },
-	{ "address",   "ADDRESS",   SIMPLE, LEVEL_MAIN },
-	{ "editor",    "EDITOR",    PERSON, LEVEL_MAIN },
-	{ "year",      "YEAR",      SIMPLE, LEVEL_MAIN }, /* date granted */
-	{ "month",     "MONTH",     SIMPLE, LEVEL_MAIN },
-	{ "day",       "DAY",       SIMPLE, LEVEL_MAIN },
-	{ "yearfiled", "YEAR",      SIMPLE, LEVEL_MAIN }, /* date filed */
-	{ "monthfiled","MONTH",     SIMPLE, LEVEL_MAIN },
-	{ "dayfiled",  "DAY",       SIMPLE, LEVEL_MAIN },
-	{ "abstract",  "ABSTRACT",  SIMPLE, LEVEL_MAIN },
-	{ "contents",  "CONTENTS",  SIMPLE, LEVEL_MAIN },
-	{ "language",  "LANGUAGE",  SIMPLE, LEVEL_MAIN },
-	{ "location",  "LOCATION",  SIMPLE, LEVEL_MAIN },
-	{ "nationality", "NATIONALITY", SIMPLE, LEVEL_MAIN },
-	{ "note",      "NOTES",     BT_NOTE, LEVEL_MAIN },
-	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
-	{ "key",       "BIBKEY",       SIMPLE, LEVEL_MAIN },
-	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",      BT_URL, LEVEL_MAIN },
-	{ "url",       "",      BT_URL, LEVEL_MAIN },
-	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
-	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
-	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
-	{ "howpublished", "",    HOWPUBLISHED, LEVEL_MAIN },
-	{ "type",       "GENRE",     SIMPLE, LEVEL_MAIN },
-	{ "refnum",    "REFNUM",    SIMPLE, LEVEL_MAIN },
-	{ "crossref",     "CROSSREF",  SIMPLE, LEVEL_MAIN },
-	{ "keywords",     "KEYWORD",   KEYWORD, LEVEL_MAIN },
+	{ "author",       "AUTHOR",      PERSON,       LEVEL_MAIN },
+	{ "translator",   "TRANSLATOR",  PERSON,       LEVEL_MAIN },
+	{ "assignee",     "ASSIGNEE",    PERSON,       LEVEL_MAIN },
+	{ "title",        "",            TITLE,        LEVEL_MAIN },
+	{ "publisher",    "PUBLISHER",   SIMPLE,       LEVEL_MAIN },
+	{ "address",      "ADDRESS",     SIMPLE,       LEVEL_MAIN },
+	{ "editor",       "EDITOR",      PERSON,       LEVEL_MAIN },
+	{ "year",         "DATE:YEAR",   SIMPLE,       LEVEL_MAIN }, /* date granted */
+	{ "month",        "DATE:MONTH",  SIMPLE,       LEVEL_MAIN },
+	{ "day",          "DATE:DAY",    SIMPLE,       LEVEL_MAIN },
+	{ "yearfiled",    "DATE:YEAR",   SIMPLE,       LEVEL_MAIN }, /* date filed */
+	{ "monthfiled",   "DATE:MONTH",  SIMPLE,       LEVEL_MAIN },
+	{ "dayfiled",     "DATE:DAY",    SIMPLE,       LEVEL_MAIN },
+	{ "abstract",     "ABSTRACT",    SIMPLE,       LEVEL_MAIN },
+	{ "contents",     "CONTENTS",    SIMPLE,       LEVEL_MAIN },
+	{ "language",     "LANGUAGE",    SIMPLE,       LEVEL_MAIN },
+	{ "location",     "LOCATION",    SIMPLE,       LEVEL_MAIN },
+	{ "nationality",  "NATIONALITY", SIMPLE,       LEVEL_MAIN },
+	{ "note",         "NOTES",       NOTES,        LEVEL_MAIN },
+	{ "annote",       "ANNOTE",      SIMPLE,       LEVEL_MAIN },
+	{ "key",          "BIBKEY",      SIMPLE,       LEVEL_MAIN },
+	{ "doi",          "DOI",         SIMPLE,       LEVEL_MAIN },
+	{ "ftp",          "",            URL,          LEVEL_MAIN },
+	{ "url",          "",            URL,          LEVEL_MAIN },
+	{ "pdf",          "FILEATTACH",  SIMPLE,       LEVEL_MAIN },
+	{ "sentelink",    "FILEATTACH",  BT_SENTE,     LEVEL_MAIN },
+	{ "file",         "FILEATTACH",  LINKEDFILE,   LEVEL_MAIN },
+	{ "howpublished", "",            HOWPUBLISHED, LEVEL_MAIN },
+	{ "type",         "GENRE",       SIMPLE,       LEVEL_MAIN },
+	{ "refnum",       "REFNUM",      SIMPLE,       LEVEL_MAIN },
+	{ "crossref",     "CROSSREF",    SIMPLE,       LEVEL_MAIN },
+	{ "keywords",     "KEYWORD",     KEYWORD,      LEVEL_MAIN },
 	{ "",         "RESOURCE|text",        ALWAYS, LEVEL_MAIN },
 	{ "",         "INTERNAL_TYPE|PATENT", ALWAYS, LEVEL_MAIN },
 	{ "",         "GENRE|patent",         ALWAYS, LEVEL_MAIN },
@@ -581,70 +589,70 @@
  * Electronic Source
  */
 static lookups electronic[] = {
-	{ "author",    "AUTHOR",    PERSON, LEVEL_MAIN },
-	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "organization", "ORGANIZER:CORP", BT_ORG, LEVEL_MAIN },
-	{ "title",     "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "booktitle", "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "publisher", "PUBLISHER", SIMPLE, LEVEL_MAIN },
-	{ "address",   "ADDRESS",   SIMPLE, LEVEL_MAIN },
-	{ "editor",    "EDITOR",    PERSON, LEVEL_MAIN },
-	{ "year",      "YEAR",      SIMPLE, LEVEL_MAIN },
-	{ "month",     "MONTH",     SIMPLE, LEVEL_MAIN },
-	{ "day",       "DAY",       SIMPLE, LEVEL_MAIN },
-	{ "abstract",  "ABSTRACT",  SIMPLE, LEVEL_MAIN },
-	{ "volume",    "VOLUME",    SIMPLE, LEVEL_MAIN },
-	{ "contents",  "CONTENTS",  SIMPLE, LEVEL_MAIN },
-	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",      BT_URL, LEVEL_MAIN },
-	{ "url",       "",      BT_URL, LEVEL_MAIN },
-	{ "pdf",          "FILEATTACH",SIMPLE, LEVEL_MAIN },
-	{ "sentelink",    "FILEATTACH",BT_SENTE, LEVEL_MAIN },
-	{ "file",         "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
-	{ "howpublished", "",    HOWPUBLISHED, LEVEL_MAIN },
-	{ "refnum",    "REFNUM",    SIMPLE, LEVEL_MAIN },
-	{ "crossref",     "CROSSREF",  SIMPLE, LEVEL_MAIN },
-	{ "location",  "LOCATION",  SIMPLE, LEVEL_MAIN },
-	{ "note",      "NOTES",     BT_NOTE, LEVEL_MAIN },
-	{ "annote",       "ANNOTE",    SIMPLE, LEVEL_MAIN },
-	{ "key",       "BIBKEY",       SIMPLE, LEVEL_MAIN },
-	{ "language",  "LANGUAGE",  SIMPLE, LEVEL_MAIN },
-	{ "keywords",     "KEYWORD",   KEYWORD, LEVEL_MAIN },
+	{ "author",       "AUTHOR",         PERSON,       LEVEL_MAIN },
+	{ "translator",   "TRANSLATOR",     PERSON,       LEVEL_MAIN },
+	{ "organization", "ORGANIZER:CORP", BT_ORG,       LEVEL_MAIN },
+	{ "title",        "",               TITLE,        LEVEL_MAIN },
+	{ "booktitle",    "",               TITLE,        LEVEL_MAIN },
+	{ "publisher",    "PUBLISHER",      SIMPLE,       LEVEL_MAIN },
+	{ "address",      "ADDRESS",        SIMPLE,       LEVEL_MAIN },
+	{ "editor",       "EDITOR",         PERSON,       LEVEL_MAIN },
+	{ "year",         "DATE:YEAR",      SIMPLE,       LEVEL_MAIN },
+	{ "month",        "DATE:MONTH",     SIMPLE,       LEVEL_MAIN },
+	{ "day",          "DATE:DAY",       SIMPLE,       LEVEL_MAIN },
+	{ "abstract",     "ABSTRACT",       SIMPLE,       LEVEL_MAIN },
+	{ "volume",       "VOLUME",         SIMPLE,       LEVEL_MAIN },
+	{ "contents",     "CONTENTS",       SIMPLE,       LEVEL_MAIN },
+	{ "doi",          "DOI",            SIMPLE,       LEVEL_MAIN },
+	{ "ftp",          "",               URL,          LEVEL_MAIN },
+	{ "url",          "",               URL,          LEVEL_MAIN },
+	{ "pdf",          "FILEATTACH",     SIMPLE,       LEVEL_MAIN },
+	{ "sentelink",    "FILEATTACH",     BT_SENTE,     LEVEL_MAIN },
+	{ "file",         "FILEATTACH",     LINKEDFILE,   LEVEL_MAIN },
+	{ "howpublished", "",               HOWPUBLISHED, LEVEL_MAIN },
+	{ "refnum",       "REFNUM",         SIMPLE,       LEVEL_MAIN },
+	{ "crossref",     "CROSSREF",       SIMPLE,       LEVEL_MAIN },
+	{ "location",     "LOCATION",       SIMPLE,       LEVEL_MAIN },
+	{ "note",         "NOTES",          NOTES,        LEVEL_MAIN },
+	{ "annote",       "ANNOTE",         SIMPLE,       LEVEL_MAIN },
+	{ "key",          "BIBKEY",         SIMPLE,       LEVEL_MAIN },
+	{ "language",     "LANGUAGE",       SIMPLE,       LEVEL_MAIN },
+	{ "keywords",     "KEYWORD",        KEYWORD,      LEVEL_MAIN },
         { "",         "RESOURCE|software, multimedia", ALWAYS, LEVEL_MAIN },
 	{ "",         "GENRE|electronic",              ALWAYS, LEVEL_MAIN },
 };
 
 static lookups misc[] = {
-	{ "author",    "AUTHOR",    PERSON, LEVEL_MAIN },
-	{ "translator",   "TRANSLATOR",PERSON, LEVEL_MAIN },
-	{ "title",     "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "booktitle", "TITLE",     TITLE,  LEVEL_MAIN },
-	{ "publisher", "PUBLISHER", SIMPLE, LEVEL_MAIN },
-	{ "organization", "ORGANIZER:CORP", BT_ORG, LEVEL_MAIN },
-	{ "pages",     "PAGES",     PAGES,  LEVEL_MAIN },
-	{ "address",   "ADDRESS",   SIMPLE, LEVEL_MAIN },
-	{ "editor",    "EDITOR",    PERSON, LEVEL_MAIN },
-	{ "year",      "YEAR",      SIMPLE, LEVEL_MAIN },
-	{ "month",     "MONTH",     SIMPLE, LEVEL_MAIN },
-	{ "day",       "DAY",       SIMPLE, LEVEL_MAIN },
-	{ "abstract",  "ABSTRACT",  SIMPLE, LEVEL_MAIN },
-	{ "contents",  "CONTENTS",  SIMPLE, LEVEL_MAIN },
-	{ "doi",       "DOI",       SIMPLE, LEVEL_MAIN },
-	{ "ftp",       "",      BT_URL, LEVEL_MAIN },
-	{ "url",       "",      BT_URL, LEVEL_MAIN },
-	{ "pdf",       "FILEATTACH",   SIMPLE, LEVEL_MAIN },
-	{ "sentelink", "FILEATTACH",BT_SENTE, LEVEL_MAIN },
-	{ "file",      "FILEATTACH",LINKEDFILE,   LEVEL_MAIN },
-	{ "howpublished", "",    HOWPUBLISHED, LEVEL_MAIN },
-	{ "refnum",    "REFNUM",       SIMPLE, LEVEL_MAIN },
-	{ "crossref",  "CROSSREF",     SIMPLE, LEVEL_MAIN },
-	{ "location",  "LOCATION",     SIMPLE, LEVEL_MAIN },
-	{ "note",      "NOTES",        BT_NOTE, LEVEL_MAIN },
-	{ "annote",    "ANNOTE",       SIMPLE, LEVEL_MAIN },
-	{ "key",       "BIBKEY",       SIMPLE, LEVEL_MAIN },
-	{ "language",  "LANGUAGE",     SIMPLE, LEVEL_MAIN },
-	{ "keywords",  "KEYWORD",      KEYWORD, LEVEL_MAIN },
-	{ "",          "INTERNAL_TYPE|MISC",       ALWAYS, LEVEL_MAIN },
+	{ "author",       "AUTHOR",         PERSON,       LEVEL_MAIN },
+	{ "translator",   "TRANSLATOR",     PERSON,       LEVEL_MAIN },
+	{ "title",        "",               TITLE,        LEVEL_MAIN },
+	{ "booktitle",    "",               TITLE,        LEVEL_MAIN },
+	{ "publisher",    "PUBLISHER",      SIMPLE,       LEVEL_MAIN },
+	{ "organization", "ORGANIZER:CORP", BT_ORG,       LEVEL_MAIN },
+	{ "pages",        "PAGES",          PAGES,        LEVEL_MAIN },
+	{ "address",      "ADDRESS",        SIMPLE,       LEVEL_MAIN },
+	{ "editor",       "EDITOR",         PERSON,       LEVEL_MAIN },
+	{ "year",         "DATE:YEAR",      SIMPLE,       LEVEL_MAIN },
+	{ "month",        "DATE:MONTH",     SIMPLE,       LEVEL_MAIN },
+	{ "day",          "DATE:DAY",       SIMPLE,       LEVEL_MAIN },
+	{ "abstract",     "ABSTRACT",       SIMPLE,       LEVEL_MAIN },
+	{ "contents",     "CONTENTS",       SIMPLE,       LEVEL_MAIN },
+	{ "doi",          "DOI",            SIMPLE,       LEVEL_MAIN },
+	{ "ftp",          "",               URL,          LEVEL_MAIN },
+	{ "url",          "",               URL,          LEVEL_MAIN },
+	{ "pdf",          "FILEATTACH",     SIMPLE,       LEVEL_MAIN },
+	{ "sentelink",    "FILEATTACH",     BT_SENTE,     LEVEL_MAIN },
+	{ "file",         "FILEATTACH",     LINKEDFILE,   LEVEL_MAIN },
+	{ "howpublished", "",               HOWPUBLISHED, LEVEL_MAIN },
+	{ "refnum",       "REFNUM",         SIMPLE,       LEVEL_MAIN },
+	{ "crossref",     "CROSSREF",       SIMPLE,       LEVEL_MAIN },
+	{ "location",     "LOCATION",       SIMPLE,       LEVEL_MAIN },
+	{ "note",         "NOTES",          NOTES,        LEVEL_MAIN },
+	{ "annote",       "ANNOTE",         SIMPLE,       LEVEL_MAIN },
+	{ "key",          "BIBKEY",         SIMPLE,       LEVEL_MAIN },
+	{ "language",     "LANGUAGE",       SIMPLE,       LEVEL_MAIN },
+	{ "keywords",     "KEYWORD",        KEYWORD,      LEVEL_MAIN },
+	{ "",             "INTERNAL_TYPE|MISC",       ALWAYS, LEVEL_MAIN },
 };
 
 #define ORIG(a) ( &(a[0]) )
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-2013
+ * Copyright (c) Chris Putnam 2005-2017
  *
  * Source code released under the GPL version 2
  *
@@ -9,26 +9,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "bibutils.h"
-
-/* bibl format includes */
-#include "adsout.h"
-#include "biblatexin.h"
-#include "bibtexin.h"
-#include "bibtexout.h"
-#include "copacin.h"
-#include "ebiin.h"
-#include "endin.h"
-#include "endxmlin.h"
-#include "endout.h"
-#include "isiin.h"
-#include "isiout.h"
-#include "medin.h"
-#include "modsin.h"
-#include "modsout.h"
-#include "risin.h"
-#include "risout.h"
-#include "wordin.h"
-#include "wordout.h"
+#include "bibformats.h"
 
 void
 bibl_initparams( param *p, int readmode, int writemode, char *progname )
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-2014
+ * Copyright (c) Chris Putnam 2005-2017
  *
  * Source code released under GPL version 2
  *
@@ -15,9 +15,9 @@
 
 #include <stdio.h>
 #include "bibl.h"
-#include "list.h"
+#include "slist.h"
 #include "charsets.h"
-#include "newstr_conv.h"
+#include "str_conv.h"
 
 #define BIBL_OK           (0)
 #define BIBL_ERR_BADINPUT (-1)
@@ -36,7 +36,8 @@
 #define BIBL_BIBLATEXIN   (BIBL_FIRSTIN+8)
 #define BIBL_EBIIN        (BIBL_FIRSTIN+9)
 #define BIBL_WORDIN       (BIBL_FIRSTIN+10)
-#define BIBL_LASTIN       (BIBL_FIRSTIN+10)
+#define BIBL_NBIBIN       (BIBL_FIRSTIN+11)
+#define BIBL_LASTIN       (BIBL_FIRSTIN+11)
 
 #define BIBL_FIRSTOUT     (200)
 #define BIBL_MODSOUT      (BIBL_FIRSTOUT)
@@ -48,7 +49,16 @@
 #define BIBL_ADSABSOUT    (BIBL_FIRSTOUT+6)
 #define BIBL_LASTOUT      (BIBL_FIRSTOUT+6)
 
-#define BIBL_FORMAT_VERBOSE (1)
+#define BIBL_FORMAT_VERBOSE             (1)
+#define BIBL_FORMAT_BIBOUT_FINALCOMMA   (2)
+#define BIBL_FORMAT_BIBOUT_SINGLEDASH   (4)
+#define BIBL_FORMAT_BIBOUT_WHITESPACE   (8)
+#define BIBL_FORMAT_BIBOUT_BRACKETS    (16)
+#define BIBL_FORMAT_BIBOUT_UPPERCASE   (32)
+#define BIBL_FORMAT_BIBOUT_STRICTKEY   (64)
+#define BIBL_FORMAT_BIBOUT_SHORTTITLE (128)
+#define BIBL_FORMAT_BIBOUT_DROPKEY    (256)
+#define BIBL_FORMAT_MODSOUT_DROPKEY   (512)
 
 #define BIBL_RAW_WITHCHARCONVERT (4)
 #define BIBL_RAW_WITHMAKEREFID   (8)
@@ -64,9 +74,9 @@
 #define BIBL_SRC_FILE    (1)  /* value from file, priority over default */
 #define BIBL_SRC_USER    (2)  /* value from user, priority over file, default */
 
-#define BIBL_XMLOUT_FALSE    NEWSTR_CONV_XMLOUT_FALSE
-#define BIBL_XMLOUT_TRUE     NEWSTR_CONV_XMLOUT_TRUE
-#define BIBL_XMLOUT_ENTITIES NEWSTR_CONV_XMLOUT_ENTITIES
+#define BIBL_XMLOUT_FALSE    STR_CONV_XMLOUT_FALSE
+#define BIBL_XMLOUT_TRUE     STR_CONV_XMLOUT_TRUE
+#define BIBL_XMLOUT_ENTITIES STR_CONV_XMLOUT_ENTITIES
 
 typedef unsigned char uchar;
 
@@ -95,20 +105,20 @@
 	uchar verbose;
 	uchar singlerefperfile;
 
-	list asis;  /* Names that shouldn't be mangled */
-	list corps; /* Names that shouldn't be mangled-MODS corporation type */
+	slist asis;  /* Names that shouldn't be mangled */
+	slist corps; /* Names that shouldn't be mangled-MODS corporation type */
 
 	char *progname;
 
 
-        int  (*readf)(FILE*,char*,int,int*,newstr*,newstr*,int*);
-        int  (*processf)(fields*,char*,char*,long);
+        int  (*readf)(FILE*,char*,int,int*,str*,str*,int*);
+        int  (*processf)(fields*,char*,char*,long,struct param*);
         int  (*cleanf)(bibl*,struct param*);
-        int  (*typef) (fields*,char*,int,struct param*,variants*,int);
-        int  (*convertf)(fields*,fields*,int,struct param*,variants*,int);
+        int  (*typef) (fields*,char*,int,struct param*);
+        int  (*convertf)(fields*,fields*,int,struct param*);
         void (*headerf)(FILE*,struct param*);
         void (*footerf)(FILE*);
-        void (*writef)(fields*,FILE*,struct param*,unsigned long);
+        int  (*writef)(fields*,FILE*,struct param*,unsigned long);
         variants *all;
         int  nall;
 
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-2013
- * Copyright (c) Johannes Wilm 2010-2013
+ * Copyright (c) Chris Putnam 2008-2017
+ * Copyright (c) Johannes Wilm 2010-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -47,7 +47,7 @@
 	{ "issuesubtitle",   "SUBTITLE",               TITLE,           LEVEL_SERIES }, /*WRONG*/
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "origlanguage",    "LANGUAGE",               SIMPLE,          LEVEL_ORIG   },
-	{ "origyear",        "YEAR",                   SIMPLE,          LEVEL_ORIG   },
+	{ "origyear",        "DATE:YEAR",              SIMPLE,          LEVEL_ORIG   },
 	{ "origdate",        "DATE",                   SIMPLE,          LEVEL_ORIG   },
 	{ "origtitle",       "TITLE",                  SIMPLE,          LEVEL_ORIG   },
 	{ "origlocation",    "ADDRESS",                SIMPLE,          LEVEL_ORIG   },
@@ -58,22 +58,23 @@
 	{ "eid",             "EID",                    SIMPLE,          LEVEL_MAIN   },
 	{ "issue",           "ISSUE",                  SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
-	{ "day",             "PARTDAY",                SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "PARTMONTH",              SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "PARTYEAR",               SIMPLE,          LEVEL_MAIN   },
+	{ "day",             "PARTDATE:DAY",           SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "PARTDATE:MONTH",         SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "PARTDATE:YEAR",          SIMPLE,          LEVEL_MAIN   },
 	{ "pages",           "PAGES",                  PAGES,           LEVEL_MAIN   },
 	{ "version",         "EDITION",                SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "issn",            "ISSN",                   SIMPLE,          LEVEL_HOST   },
+	{ "coden",           "CODEN",                  SIMPLE,          LEVEL_HOST   },
 	{ "abstract",        "ABSTRACT",               SIMPLE,          LEVEL_MAIN   },
 	{ "addendum",        "ADDENDUM",               SIMPLE,          LEVEL_MAIN   },
 	{ "doi",             "DOI",                    SIMPLE,          LEVEL_MAIN   },
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?urlday?",               SIMPLE,          LEVEL_MAIN   }, /* WRONG */
 	{ "urlmonth",        "?urlmonth?",             SIMPLE,          LEVEL_MAIN   }, /* WRONG */
@@ -120,11 +121,11 @@
 	{ "mainsubtitle",    "SUBTITLE",               TITLE,           LEVEL_HOST   },
 	{ "maintitleaddon",  "TITLEADDON",             TITLE,           LEVEL_HOST   },
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "origlanguage",    "LANGUAGE",               SIMPLE,          LEVEL_ORIG   },
-	{ "origyear",        "YEAR",                   SIMPLE,          LEVEL_ORIG   },
+	{ "origyear",        "DATE:YEAR",              SIMPLE,          LEVEL_ORIG   },
 	{ "origdate",        "DATE",                   SIMPLE,          LEVEL_ORIG   },
 	{ "origtitle",       "TITLE",                  SIMPLE,          LEVEL_ORIG   },
 	{ "origlocation",    "ADDRESS",                SIMPLE,          LEVEL_ORIG   },
@@ -135,7 +136,7 @@
 	{ "volumes",         "NUMVOLUMES",             SIMPLE,          LEVEL_HOST   },
 	{ "series",          "TITLE",                  SIMPLE,          LEVEL_HOST   }, /* WRONG */
 	{ "number",          "NUMBER",                 SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "publisher",       "PUBLISHER",              SIMPLE,          LEVEL_MAIN   },
@@ -143,13 +144,13 @@
 	{ "isbn",            "ISBN",                   SIMPLE,          LEVEL_MAIN   },
 	{ "chapter",         "CHAPTER",                SIMPLE,          LEVEL_MAIN   },
 	{ "pages",           "PAGES",                  PAGES,           LEVEL_MAIN   }, /* WRONG */
-	{ "pagetotal",       "TOTALPAGES",             SIMPLE,          LEVEL_MAIN   },
+	{ "pagetotal",       "PAGES:TOTAL",            SIMPLE,          LEVEL_MAIN   },
 	{ "addendum",        "ADDENDUM",               SIMPLE,          LEVEL_MAIN   },
 	{ "doi",             "DOI",                    SIMPLE,          LEVEL_MAIN   },
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?urlday?",               SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?urlmonth?",             SIMPLE,          LEVEL_MAIN   },
@@ -184,25 +185,25 @@
 	{ "titleaddon",      "TITLEADDON",             TITLE,           LEVEL_MAIN   },
 	{ "shorttitle",      "SHORTTITLE",             SIMPLE,          LEVEL_MAIN   },
 	{ "howpublished",    "",                       HOWPUBLISHED,    LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "type",            "GENRE",                  BT_GENRE,        LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "publisher",       "PUBLISHER",              SIMPLE,          LEVEL_MAIN   },
 	{ "location",        "ADDRESS",                SIMPLE,          LEVEL_MAIN   },
 	{ "chapter",         "CHAPTER",                SIMPLE,          LEVEL_MAIN   },
 	{ "pages",           "PAGES",                  PAGES,           LEVEL_MAIN   },
-	{ "pagetotal",       "TOTALPAGES",             SIMPLE,          LEVEL_MAIN   },
+	{ "pagetotal",       "PAGES:TOTAL",            SIMPLE,          LEVEL_MAIN   },
 	{ "addendum",        "ADDENDUM",               SIMPLE,          LEVEL_MAIN   },
 	{ "doi",             "DOI",                    SIMPLE,          LEVEL_MAIN   },
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?urlday?",               SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?urlmonth?",             SIMPLE,          LEVEL_MAIN   },
@@ -233,8 +234,8 @@
 	{ "annotator",       "ANNOTATOR",              PERSON,          LEVEL_MAIN   },
 	{ "commentator",     "COMMENTATOR",            PERSON,          LEVEL_MAIN   },
 	{ "translator",      "TRANSLATOR",             PERSON,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "introduction",    "INTROAUTHOR",            PERSON,          LEVEL_MAIN   },
 	{ "foreword",        "INTROAUTHOR",            PERSON,          LEVEL_MAIN   },
@@ -248,7 +249,7 @@
 	{ "maintitleaddon",  "TITLEADDON",             TITLE,           LEVEL_HOST   },
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "origlanguage",    "LANGUAGE",               SIMPLE,          LEVEL_ORIG   },
-	{ "origyear",        "YEAR",                   SIMPLE,          LEVEL_ORIG   },
+	{ "origyear",        "DATE:YEAR",              SIMPLE,          LEVEL_ORIG   },
 	{ "origdate",        "DATE",                   SIMPLE,          LEVEL_ORIG   },
 	{ "origtitle",       "TITLE",                  SIMPLE,          LEVEL_ORIG   },
 	{ "origlocation",    "ADDRESS",                SIMPLE,          LEVEL_ORIG   },
@@ -259,7 +260,7 @@
 	{ "volumes",         "NUMVOLUMES",             SIMPLE,          LEVEL_HOST   },
 	{ "series",          "TITLE",                  SIMPLE,          LEVEL_HOST   },
 	{ "number",          "NUMBER",                 SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "publisher",       "PUBLISHER",              SIMPLE,          LEVEL_MAIN   },
@@ -267,13 +268,13 @@
 	{ "isbn",            "ISBN",                   SIMPLE,          LEVEL_MAIN   },
 	{ "chapter",         "CHAPTER",                SIMPLE,          LEVEL_MAIN   },
 	{ "pages",           "PAGES",                  PAGES,           LEVEL_MAIN   },
-	{ "pagetotal",       "TOTALPAGES",             SIMPLE,          LEVEL_MAIN   },
+	{ "pagetotal",       "PAGES:TOTAL",            SIMPLE,          LEVEL_MAIN   },
 	{ "addendum",        "ADDENDUM",               SIMPLE,          LEVEL_MAIN   },
 	{ "doi",             "DOI",                    SIMPLE,          LEVEL_MAIN   },
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?urlday?",               SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?urlmonth?",             SIMPLE,          LEVEL_MAIN   },
@@ -307,8 +308,8 @@
 	{ "annotator",       "ANNOTATOR",              PERSON,          LEVEL_HOST   },
 	{ "commentator",     "COMMENTATOR",            PERSON,          LEVEL_HOST   },
 	{ "translator",      "TRANSLATOR",             PERSON,          LEVEL_HOST   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "introduction",    "INTROAUTHOR",            PERSON,          LEVEL_HOST   },
 	{ "foreword",        "INTROAUTHOR",            PERSON,          LEVEL_HOST   },
@@ -326,7 +327,7 @@
 	{ "maintitleaddon",  "TITLEADDON",             TITLE,           LEVEL_SERIES },
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "origlanguage",    "LANGUAGE",               SIMPLE,          LEVEL_ORIG   },
-	{ "origyear",        "YEAR",                   SIMPLE,          LEVEL_ORIG   },
+	{ "origyear",        "DATE:YEAR",              SIMPLE,          LEVEL_ORIG   },
 	{ "origdate",        "DATE",                   SIMPLE,          LEVEL_ORIG   },
 	{ "origtitle",       "TITLE",                  SIMPLE,          LEVEL_ORIG   },
 	{ "origlocation",    "ADDRESS",                SIMPLE,          LEVEL_ORIG   },
@@ -338,7 +339,7 @@
 	{ "volumes",         "NUMVOLUMES",             SIMPLE,          LEVEL_HOST   },
 	{ "series",          "TITLE",                  SIMPLE,          LEVEL_SERIES+1 }, /* WRONG */
 	{ "number",          "NUMBER",                 SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "publisher",       "PUBLISHER",              SIMPLE,          LEVEL_MAIN   },
@@ -350,7 +351,7 @@
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?urlday?",               SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?urlmonth?",             SIMPLE,          LEVEL_MAIN   },
@@ -369,8 +370,8 @@
 
 static lookups incollection[] = {
 	{ "author",          "AUTHOR",                 PERSON,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "title",           "TITLE",                  TITLE,           LEVEL_MAIN   },
 	{ "subtitle",        "SUBTITLE",               TITLE,           LEVEL_MAIN   },
@@ -379,7 +380,7 @@
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "edition",         "EDITION",                SIMPLE,          LEVEL_MAIN   },
 	{ "number",          "NUMBER",                 SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "introduction",    "INTROAUTHOR",            PERSON,          LEVEL_HOST   },
@@ -416,7 +417,7 @@
 	{ "series",          "TITLE",                  SIMPLE,          LEVEL_SERIES+1 }, /* WRONG */
 
 	{ "origlanguage",    "LANGUAGE",               SIMPLE,          LEVEL_ORIG   },
-	{ "origyear",        "YEAR",                   SIMPLE,          LEVEL_ORIG   },
+	{ "origyear",        "DATE:YEAR",              SIMPLE,          LEVEL_ORIG   },
 	{ "origdate",        "DATE",                   SIMPLE,          LEVEL_ORIG   },
 	{ "origtitle",       "TITLE",                  SIMPLE,          LEVEL_ORIG   },
 	{ "origlocation",    "ADDRESS",                SIMPLE,          LEVEL_ORIG   },
@@ -432,7 +433,7 @@
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?urlday?",               SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?urlmonth?",             SIMPLE,          LEVEL_MAIN   },
@@ -467,8 +468,8 @@
 	{ "commentator",     "COMMENTATOR",            PERSON,          LEVEL_HOST   },
 	{ "translator",      "TRANSLATOR",             PERSON,          LEVEL_HOST   },
 	{ "eventtitle",      "EVENT:CONF",             SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "introduction",    "INTROAUTHOR",            PERSON,          LEVEL_HOST   },
 	{ "foreword",        "INTROAUTHOR",            PERSON,          LEVEL_HOST   },
@@ -493,7 +494,7 @@
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "origlanguage",    "LANGUAGE",               SIMPLE,          LEVEL_ORIG   },
 	{ "origdate",        "DATE",                   SIMPLE,          LEVEL_ORIG   },
-	{ "origyear",        "YEAR",                   SIMPLE,          LEVEL_ORIG   },
+	{ "origyear",        "DATE:YEAR",              SIMPLE,          LEVEL_ORIG   },
 	{ "origtitle",       "TITLE",                  SIMPLE,          LEVEL_ORIG   },
 	{ "origlocation",    "ADDRESS",                SIMPLE,          LEVEL_ORIG   },
 	{ "origpublisher",   "PUBLISHER",              SIMPLE,          LEVEL_ORIG   },
@@ -502,7 +503,7 @@
 	{ "edition",         "EDITION",                SIMPLE,          LEVEL_MAIN   },
 	{ "volumes",         "NUMVOLUMES",             SIMPLE,          LEVEL_HOST   },
 	{ "number",          "NUMBER",                 SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "publisher",       "PUBLISHER",              SIMPLE,          LEVEL_HOST   },
@@ -515,7 +516,7 @@
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?urlday?",               SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?urlmonth?",             SIMPLE,          LEVEL_MAIN   },
@@ -547,8 +548,8 @@
 	{ "annotator",       "ANNOTATOR",              PERSON,          LEVEL_MAIN   },
 	{ "commentator",     "COMMENTATOR",            PERSON,          LEVEL_MAIN   },
 	{ "translator",      "TRANSLATOR",             PERSON,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   }, /*WRONG*/
 	{ "introduction",    "INTROAUTHOR",            PERSON,          LEVEL_MAIN   },
 	{ "foreword",        "INTROAUTHOR",            PERSON,          LEVEL_MAIN   },
@@ -563,7 +564,7 @@
 	{ "type",            "GENRE",                  BT_GENRE,        LEVEL_MAIN   },
 	{ "series",          "TITLE",                  SIMPLE,          LEVEL_HOST   },
 	{ "number",          "NUMBER",                 SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "organization",    "ORGANIZER:CORP",         SIMPLE,          LEVEL_MAIN   },
@@ -572,13 +573,13 @@
 	{ "isbn",            "ISBN",                   SIMPLE,          LEVEL_MAIN   },
 	{ "chapter",         "CHAPTER",                SIMPLE,          LEVEL_MAIN   },
 	{ "pages",           "PAGES",                  PAGES,           LEVEL_MAIN   },
-	{ "pagetotal",       "TOTALPAGES",             SIMPLE,          LEVEL_MAIN   },
+	{ "pagetotal",       "PAGES:TOTAL",            SIMPLE,          LEVEL_MAIN   },
 	{ "addendum",        "ADDENDUM",               SIMPLE,          LEVEL_MAIN   },
 	{ "doi",             "DOI",                    SIMPLE,          LEVEL_MAIN   },
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?????????",              SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?????????",              SIMPLE,          LEVEL_MAIN   },
@@ -609,15 +610,15 @@
 	{ "subtitle",        "SUBTITLE",               TITLE,           LEVEL_MAIN   },
 	{ "titleaddon",      "TITLEADDON",             TITLE,           LEVEL_MAIN   },
 	{ "shorttitle",      "SHORTTITLE",             SIMPLE,          LEVEL_MAIN   },
-	{ "day",             "DAY",                    SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
+	{ "day",             "DATE:DAY",               SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "howpublished",    "",                       HOWPUBLISHED,    LEVEL_MAIN   },
 	{ "version",         "EDITION",                SIMPLE,          LEVEL_MAIN   },
 	{ "type",            "GENRE",                  BT_GENRE,        LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "organization",    "ORGANIZER:CORP",         SIMPLE,          LEVEL_MAIN   },
@@ -629,7 +630,7 @@
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?????????",              SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?????????",              SIMPLE,          LEVEL_MAIN   },
@@ -658,13 +659,13 @@
 	{ "titleaddon",      "TITLEADDON",             TITLE,           LEVEL_MAIN   },
 	{ "shorttitle",      "SHORTTITLE",             SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },
-	{ "day",             "DAY",                    SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
+	{ "day",             "DATE:DAY",               SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "version",         "EDITION",                SIMPLE,          LEVEL_MAIN   },
 	{ "type",            "GENRE",                  BT_GENRE,        LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "organization",    "ORGANIZER:CORP",         SIMPLE,          LEVEL_MAIN   },
@@ -675,7 +676,7 @@
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?????????",              SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?????????",              SIMPLE,          LEVEL_MAIN   },
@@ -695,19 +696,19 @@
 	{ "titleaddon",      "TITLEADDON",             TITLE,           LEVEL_MAIN   },
 	{ "shorttitle",      "SHORTTITLE",             SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },
-	{ "day",             "DAY",                    SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
+	{ "day",             "DATE:DAY",               SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
 	{ "version",         "EDITION",                SIMPLE,          LEVEL_MAIN   },
 	{ "type",            "GENRE",                  BT_GENRE,        LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "organization",    "ORGANIZER:CORP",         SIMPLE,          LEVEL_MAIN   },
 	{ "location",        "ADDRESS",                SIMPLE,          LEVEL_MAIN   },
 	{ "number",          "NUMBER",                 SIMPLE,          LEVEL_MAIN   },
 	{ "addendum",        "ADDENDUM",               SIMPLE,          LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?????????",              SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?????????",              SIMPLE,          LEVEL_MAIN   },
@@ -753,20 +754,21 @@
 	{ "number",          "NUMBER",                 SIMPLE,          LEVEL_MAIN   },
 	{ "issue",           "ISSUE",                  SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
-	{ "day",             "PARTDAY",                SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "PARTMONTH",              SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "PARTYEAR",               SIMPLE,          LEVEL_MAIN   },
+	{ "day",             "PARTDATE:DAY",           SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "PARTDATE:MONTH",         SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "PARTDATE:YEAR",          SIMPLE,          LEVEL_MAIN   },
 	{ "pages",           "PAGES",                  PAGES,           LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "issn",            "ISSN",                   SIMPLE,          LEVEL_HOST   },
+	{ "coden",           "CODEN",                  SIMPLE,          LEVEL_HOST   },
 	{ "addendum",        "ADDENDUM",               SIMPLE,          LEVEL_MAIN   },
 	{ "doi",             "DOI",                    SIMPLE,          LEVEL_MAIN   },
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?????",                  SIMPLE,          LEVEL_MAIN   }, /* WRONG */
 	{ "urlmonth",        "?????",                  SIMPLE,          LEVEL_MAIN   }, /* WRONG */
@@ -798,8 +800,8 @@
 	{ "commentator",     "COMMENTATOR",            PERSON,          LEVEL_MAIN   },
 	{ "translator",      "TRANSLATOR",             PERSON,          LEVEL_MAIN   },
 	{ "eventtitle",      "EVENT:CONF",             SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "introduction",    "INTROAUTHOR",            PERSON,          LEVEL_MAIN   },
 	{ "foreword",        "INTROAUTHOR",            PERSON,          LEVEL_MAIN   },
@@ -814,7 +816,7 @@
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "origlanguage",    "LANGUAGE",               SIMPLE,          LEVEL_ORIG   },
 	{ "origdate",        "DATE",                   SIMPLE,          LEVEL_ORIG   },
-	{ "origyear",        "YEAR",                   SIMPLE,          LEVEL_ORIG   },
+	{ "origyear",        "DATE:YEAR",              SIMPLE,          LEVEL_ORIG   },
 	{ "origtitle",       "TITLE",                  SIMPLE,          LEVEL_ORIG   },
 	{ "origlocation",    "ADDRESS",                SIMPLE,          LEVEL_ORIG   },
 	{ "origpublisher",   "PUBLISHER",              SIMPLE,          LEVEL_ORIG   },
@@ -824,7 +826,7 @@
 	{ "volumes",         "NUMVOLUMES",             SIMPLE,          LEVEL_HOST   },
 	{ "series",          "TITLE",                  SIMPLE,          LEVEL_SERIES },
 	{ "number",          "NUMBER",                 SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "organization",    "ORGANIZER:CORP",         SIMPLE,          LEVEL_MAIN   },
@@ -833,13 +835,13 @@
 	{ "isbn",            "ISBN",                   SIMPLE,          LEVEL_MAIN   },
 	{ "chapter",         "CHAPTER",                SIMPLE,          LEVEL_MAIN   },
 	{ "pages",           "PAGES",                  SIMPLE,          LEVEL_MAIN   },
-	{ "pagetotal",       "TOTALPAGES",             SIMPLE,          LEVEL_MAIN   },
+	{ "pagetotal",       "PAGES:TOTAL",            SIMPLE,          LEVEL_MAIN   },
 	{ "addendum",        "ADDENDUM",               SIMPLE,          LEVEL_MAIN   },
 	{ "doi",             "DOI",                    SIMPLE,          LEVEL_MAIN   },
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?????????",              SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?????????",              SIMPLE,          LEVEL_MAIN   },
@@ -862,12 +864,12 @@
 	{ "series",          "TITLE",                  TITLE,           LEVEL_HOST   },
 	{ "type",            "GENRE",                  BT_GENRE,        LEVEL_MAIN   },
 	{ "institution",     "SPONSOR:ASIS",           SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "number",          "REPORTNUMBER",           SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "version",         "EDITION",                SIMPLE,          LEVEL_MAIN   },
@@ -875,13 +877,13 @@
 	{ "isrn",            "ISRN",                   SIMPLE,          LEVEL_MAIN   },
 	{ "chapter",         "CHAPTER",                SIMPLE,          LEVEL_MAIN   },
 	{ "pages",           "PAGES",                  SIMPLE,          LEVEL_MAIN   },
-	{ "pagetotal",       "TOTALPAGES",             SIMPLE,          LEVEL_MAIN   },
+	{ "pagetotal",       "PAGES:TOTAL",            SIMPLE,          LEVEL_MAIN   },
 	{ "addendum",        "ADDENDUM",               SIMPLE,          LEVEL_MAIN   },
 	{ "doi",             "DOI",                    SIMPLE,          LEVEL_MAIN   },
 	{ "pubstate",        "PUBSTATE",               SIMPLE,          LEVEL_MAIN   },
 	{ "eprint",          "",                       BT_EPRINT,       LEVEL_MAIN   },
 	{ "eprinttype",      "",                       BT_EPRINT,       LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?????????",              SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?????????",              SIMPLE,          LEVEL_MAIN   },
@@ -902,16 +904,16 @@
 	{ "titleaddon",      "TITLEADDON",             TITLE,           LEVEL_MAIN   },
 	{ "shorttitle",      "SHORTTITLE",             SIMPLE,          LEVEL_MAIN   },
 	{ "howpublished",    "",                       HOWPUBLISHED,    LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
-	{ "day",             "DAY",                    SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
+	{ "day",             "DATE:DAY",               SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urlday",          "?????????",              SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?????????",              SIMPLE,          LEVEL_MAIN   },
 	{ "urlyear",         "?????????",              SIMPLE,          LEVEL_MAIN   },
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "addendum",        "ADDENDUM",               SIMPLE,          LEVEL_MAIN   },
@@ -930,23 +932,23 @@
 	{ "subtitle",        "SUBTITLE",               TITLE,           LEVEL_MAIN   },
 	{ "titleaddon",      "TITLEADDON",             TITLE,           LEVEL_MAIN   },
 	{ "shorttitle",      "SHORTTITLE",             SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
-	{ "day",             "DAY",                    SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
+	{ "day",             "DATE:DAY",               SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "type",            "",                       BLT_THESIS_TYPE, LEVEL_MAIN   },
 	{ "institution",     "DEGREEGRANTOR:ASIS",     SIMPLE,          LEVEL_MAIN   },
 	{ "school",          "DEGREEGRANTOR:ASIS",     BLT_SCHOOL,      LEVEL_MAIN   },
 	{ "doi",             "DOI",                    SIMPLE,          LEVEL_MAIN   },
 	{ "howpublished",    "",                       HOWPUBLISHED,    LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?urlday?",               SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?urlmonth?",             SIMPLE,          LEVEL_MAIN   },
 	{ "urlyear",         "?urlyear?",              SIMPLE,          LEVEL_MAIN   },
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "location",        "ADDRESS",                SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "address",         "ADDRESS",                SIMPLE,          LEVEL_MAIN   },
@@ -964,23 +966,23 @@
 	{ "subtitle",        "SUBTITLE",               TITLE,           LEVEL_MAIN   },
 	{ "titleaddon",      "TITLEADDON",             TITLE,           LEVEL_MAIN   },
 	{ "shorttitle",      "SHORTTITLE",             SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
-	{ "day",             "DAY",                    SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
+	{ "day",             "DATE:DAY",               SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "type",            "",                       BLT_THESIS_TYPE, LEVEL_MAIN   },
 	{ "institution",     "DEGREEGRANTOR:ASIS",     SIMPLE,          LEVEL_MAIN   },
 	{ "school",          "DEGREEGRANTOR:ASIS",     BLT_SCHOOL,      LEVEL_MAIN   },
 	{ "doi",             "DOI",                    SIMPLE,          LEVEL_MAIN   },
 	{ "howpublished",    "",                       HOWPUBLISHED,    LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?urlday?",               SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?urlmonth?",             SIMPLE,          LEVEL_MAIN   },
 	{ "urlyear",         "?urlyear?",              SIMPLE,          LEVEL_MAIN   },
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "location",        "ADDRESS",                SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "address",         "ADDRESS",                SIMPLE,          LEVEL_MAIN   },
@@ -999,23 +1001,23 @@
 	{ "subtitle",        "SUBTITLE",               TITLE,           LEVEL_MAIN   },
 	{ "titleaddon",      "TITLEADDON",             TITLE,           LEVEL_MAIN   },
 	{ "shorttitle",      "SHORTTITLE",             SIMPLE,          LEVEL_MAIN   },
-	{ "year",            "YEAR",                   SIMPLE,          LEVEL_MAIN   },
-	{ "month",           "MONTH",                  SIMPLE,          LEVEL_MAIN   },
-	{ "day",             "DAY",                    SIMPLE,          LEVEL_MAIN   },
+	{ "year",            "DATE:YEAR",              SIMPLE,          LEVEL_MAIN   },
+	{ "month",           "DATE:MONTH",             SIMPLE,          LEVEL_MAIN   },
+	{ "day",             "DATE:DAY",               SIMPLE,          LEVEL_MAIN   },
 	{ "date",            "DATE",                   SIMPLE,          LEVEL_MAIN   },   /*WRONG*/
 	{ "type",            "",                       BLT_THESIS_TYPE, LEVEL_MAIN   },
 	{ "institution",     "DEGREEGRANTOR:ASIS",     SIMPLE,          LEVEL_MAIN   },
 	{ "school",          "DEGREEGRANTOR:ASIS",     BLT_SCHOOL,      LEVEL_MAIN   },
 	{ "doi",             "DOI",                    SIMPLE,          LEVEL_MAIN   },
 	{ "howpublished",    "",                       HOWPUBLISHED,    LEVEL_MAIN   },
-	{ "url",             "",                       BT_URL,          LEVEL_MAIN   },
+	{ "url",             "",                       URL,             LEVEL_MAIN   },
 	{ "urldate",         "URLDATE",                SIMPLE,          LEVEL_MAIN   },
 	{ "urlday",          "?urlday?",               SIMPLE,          LEVEL_MAIN   },
 	{ "urlmonth",        "?urlmonth?",             SIMPLE,          LEVEL_MAIN   },
 	{ "urlyear",         "?urlyear?",              SIMPLE,          LEVEL_MAIN   },
 	{ "language",        "LANGUAGE",               SIMPLE,          LEVEL_MAIN   },
 	{ "location",        "ADDRESS",                SIMPLE,          LEVEL_MAIN   },
-	{ "note",            "NOTES",                  SIMPLE,          LEVEL_MAIN   },
+	{ "note",            "NOTES",                  NOTES,           LEVEL_MAIN   },
 	{ "annote",          "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "annotation",      "ANNOTATION",             SIMPLE,          LEVEL_MAIN   },
 	{ "address",         "ADDRESS",                SIMPLE,          LEVEL_MAIN   },
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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * 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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * 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-2014
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -10,19 +10,26 @@
 #include <stdlib.h>
 #include <string.h>
 #include "is_ws.h"
-#include "newstr.h"
-#include "newstr_conv.h"
-#include "list.h"
+#include "str.h"
+#include "str_conv.h"
+#include "slist.h"
 #include "name.h"
-#include "title.h"
 #include "fields.h"
 #include "reftypes.h"
-#include "serialno.h"
-#include "copacin.h"
+#include "bibformats.h"
+#include "generic.h"
 
+extern variants copac_all[];
+extern int copac_nall;
+
 /*****************************************************
  PUBLIC: void copacin_initparams()
 *****************************************************/
+
+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_convertf( fields *bibin, fields *info, int reftype, param *pm );
+
 void
 copacin_initparams( param *p, const char *progname )
 {
@@ -45,8 +52,8 @@
 	p->all      = copac_all;
 	p->nall     = copac_nall;
 
-	list_init( &(p->asis) );
-	list_init( &(p->corps) );
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
 
 	if ( !progname ) p->progname = NULL;
 	else p->progname = strdup( progname );
@@ -74,14 +81,14 @@
 	return 1; 
 }
 static int
-readmore( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line )
+readmore( FILE *fp, char *buf, int bufsize, int *bufpos, str *line )
 {
 	if ( line->len ) return 1;
-	else return newstr_fget( fp, buf, bufsize, bufpos, line );
+	else return str_fget( fp, buf, bufsize, bufpos, line );
 }
 
-int
-copacin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset )
+static int
+copacin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
 	int haveref = 0, inref=0;
 	char *p;
@@ -100,22 +107,22 @@
 			p += 3;
 		}
 		if ( copacin_istag( p ) ) {
-			if ( inref ) newstr_addchar( reference, '\n' );
-			newstr_strcat( reference, p );
-			newstr_empty( line );
+			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 */
-				newstr_addchar( reference, ' ' );
+				str_addchar( reference, ' ' );
 				if ( *p ) p++;
 				if ( *p ) p++;
 				if ( *p ) p++;
-			   	newstr_strcat( reference, p );
+				str_strcatc( reference, p );
 			}
-			newstr_empty( line );
+			str_empty( line );
 		} else {
-			newstr_empty( line );
+			str_empty( line );
 		}
 	}
 	return haveref;
@@ -126,20 +133,20 @@
 *****************************************************/
 
 static char*
-copacin_addtag2( char *p, newstr *tag, newstr *data )
+copacin_addtag2( char *p, str *tag, str *data )
 {
 	int  i;
 	i =0;
 	while ( i<3 && *p ) {
-		newstr_addchar( tag, *p++ );
+		str_addchar( tag, *p++ );
 		i++;
 	}
 	while ( *p==' ' || *p=='\t' ) p++;
 	while ( *p && *p!='\r' && *p!='\n' ) {
-		newstr_addchar( data, *p );
+		str_addchar( data, *p );
 		p++;
 	}
-	newstr_trimendingws( data );
+	str_trimendingws( data );
 	while ( *p=='\n' || *p=='\r' ) p++;
 	return p;
 }
@@ -152,29 +159,29 @@
 	return p;
 }
 
-int
-copacin_processf( fields *copacin, char *p, char *filename, long nref )
+static int
+copacin_processf( fields *copacin, char *p, char *filename, long nref, param *pm )
 {
-	newstr tag, data;
+	str tag, data;
 	int status;
-	newstr_init( &tag );
-	newstr_init( &data );
+	str_init( &tag );
+	str_init( &data );
 	while ( *p ) {
 		p = skip_ws( p );
 		if ( copacin_istag( p ) ) {
 			p = copacin_addtag2( p, &tag, &data );
 			/* don't add empty strings */
-			if ( tag.len && data.len ) {
+			if ( str_has_value( &tag ) && str_has_value( &data ) ) {
 				status = fields_add( copacin, tag.data, data.data, 0 );
 				if ( status!=FIELDS_OK ) return 0;
 			}
-			newstr_empty( &tag );
-			newstr_empty( &data );
+			str_empty( &tag );
+			str_empty( &data );
 		}
 		else p = copacin_nextline( p );
 	}
-	newstr_free( &tag );
-	newstr_free( &data );
+	str_free( &tag );
+	str_free( &data );
 	return 1;
 }
 
@@ -188,48 +195,54 @@
  * editors seem to be stuck in as authors with the tag "[Editor]" in it
  */
 static int
-copacin_addname( fields *info, char *tag, newstr *name, int level, list *asis,
-	list *corps )
+copacin_person( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
 {
-	char *usetag = tag, editor[]="EDITOR";
-	newstr usename, *s;
-	list tokens;
-	int comma = 0, i, ok;
+	char *usetag = outtag, editor[]="EDITOR";
+	int comma = 0, i, ok, status;
+	str usename, *s;
+	slist tokens;
 
-	if ( list_find( asis, name->data ) !=-1  ||
-	     list_find( corps, name->data ) !=-1 ) {
-		ok = name_add( info, tag, name->data, level, asis, corps );
+	if ( slist_find( &(pm->asis),  invalue ) !=-1  ||
+	     slist_find( &(pm->corps), invalue ) !=-1 ) {
+		ok = name_add( bibout, outtag, invalue->data, level, &(pm->asis), &(pm->corps) );
 		if ( ok ) return BIBL_OK;
 		else return BIBL_ERR_MEMERR;
 	}
 
-	list_init( &tokens );
-	newstr_init( &usename );
+	slist_init( &tokens );
+	str_init( &usename );
 
-	list_tokenize( &tokens, name, " ", 1 );
+	status = slist_tokenize( &tokens, invalue, " ", 1 );
+	if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
+
 	for ( i=0; i<tokens.n; ++i ) {
-		s = list_get( &tokens, i );
-		if ( !strcmp( s->data, "[Editor]" ) ) {
+		s = slist_str( &tokens, i );
+		if ( !strcmp( str_cstr( s ), "[Editor]" ) ) {
 			usetag = editor;
-			newstr_strcpy( s, "" );
+			str_empty( s );
 		} else if ( s->len && s->data[s->len-1]==',' ) {
 			comma++;
 		}
 	}
 
 	if ( comma==0 && tokens.n ) {
-		s = list_get( &tokens, 0 );
-		newstr_addchar( s, ',' );
+		s = slist_str( &tokens, 0 );
+		str_addchar( s, ',' );
 	}
 
 	for ( i=0; i<tokens.n; ++i ) {
-		if ( i ) newstr_addchar( &usename, ' ' );
-		newstr_newstrcat( &usename, list_get( &tokens, i ) );
+		s = slist_str( &tokens, i );
+		if ( str_is_empty( s ) ) continue;
+		if ( i ) str_addchar( &usename, ' ' );
+		str_strcat( &usename, s );
 	}
 
-	list_free( &tokens );
+	slist_free( &tokens );
 
-	ok = name_add( info, usetag, usename.data, level, asis, corps );
+	ok = name_add( bibout, usetag, str_cstr( &usename ), level, &(pm->asis), &(pm->corps) );
+
+	str_free( &usename );
+
 	if ( ok ) return BIBL_OK;
 	else return BIBL_ERR_MEMERR;
 }
@@ -244,66 +257,37 @@
 }
 
 static int
-copacin_simple( fields *out, char *tag, char *value, int level )
+copacin_convertf( fields *bibin, fields *bibout, int reftype, param *p )
 {
-	int fstatus = fields_add( out, tag, value, level );
-	if ( fstatus==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
+	static int (*convertfns[NUM_REFTYPES])(fields *, int, str *, str *, int, param *, char *, fields *) = {
+		[ 0 ... NUM_REFTYPES-1 ] = generic_null,
+		[ SIMPLE       ] = generic_simple,
+		[ TITLE        ] = generic_title,
+		[ NOTES        ] = generic_notes,
+		[ SERIALNO     ] = generic_serialno,
+		[ PERSON       ] = copacin_person
+	};
 
-int
-copacin_convertf( fields *copacin, fields *out, int reftype, param *p, variants *all, int nall )
-{
-	int  process, level, i, n, nfields, ok, status = BIBL_OK;
-	newstr *tag, *data;
-	char *newtag;
+	int  process, level, i, nfields, status = BIBL_OK;
+	str *intag, *invalue;
+	char *outtag;
 
-	nfields = fields_num( copacin );
+	nfields = fields_num( bibin );
 	for ( i=0; i<nfields; ++i ) {
 
-		tag = fields_tag( copacin, i, FIELDS_STRP );
+		intag = fields_tag( bibin, i, FIELDS_STRP );
 
-		n = translate_oldtag( tag->data, reftype, all, nall, &process, &level, &newtag );
-		if ( n==-1 ) {
-			copacin_report_notag( p, tag->data );
+		if ( !translate_oldtag( intag->data, reftype, p->all, p->nall, &process, &level, &outtag ) ) {
+			copacin_report_notag( p, intag->data );
 			continue;
 		}
-		if ( process == ALWAYS ) continue; /*add these later*/
 
-		data = fields_value( copacin, i, FIELDS_STRP );
-
-		switch ( process ) {
-
-		case SIMPLE:
-			status = copacin_simple( out, newtag, data->data, level );
-			break;
-
-		case TITLE:
-			ok = title_process( out, newtag, data->data, level, p->nosplittitle );
-			if ( ok ) status = BIBL_OK;
-			else status = BIBL_ERR_MEMERR;
-			break;
-
-		case PERSON:
-			status = copacin_addname( out, newtag, data, level, &(p->asis), &(p->corps) );
-			break;
-
-		case SERIALNO:
-			ok = addsn( out, data->data, level );
-			if ( ok ) status = BIBL_OK;
-			else status = BIBL_ERR_MEMERR;
-			break;
-
-		default:
-			fprintf(stderr,"%s: internal error -- " "illegal process value %d\n", p->progname, process );
-			status = BIBL_OK;
-			break;
-		}
+		invalue = fields_value( bibin, i, FIELDS_STRP );
 
+		status = convertfns[ process ] ( bibin, i, intag, invalue, level, p, outtag, bibout );
 		if ( status!=BIBL_OK ) return status;
 
 	}
 
 	return status;
 }
-
diff --git a/bibutils/copacin.h b/bibutils/copacin.h
deleted file mode 100644
--- a/bibutils/copacin.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * copacin.h
- *
- * Copyright (c) Chris Putnam 2004-2014
- *
- * Source code released under the GPL
- *
- */
-#ifndef COPACIN_H
-#define COPACIN_H
-
-#include "newstr.h"
-#include "fields.h"
-#include "reftypes.h"
-#include "bibutils.h"
-
-extern int copacin_readf( FILE *fp, char *buf, int bufsize, int *bufpos,
-		newstr *line, newstr *reference, int *fcharset );
-extern int copacin_processf( fields *copacin, char *p, char *filename, 
-		long nref );
-extern int copacin_convertf( fields *copacin, fields *info, int reftype, param *p, variants *all, int nall );
-
-extern void copacin_initparams( param *p, const char *progname );
-
-
-extern variants copac_all[];
-extern int copac_nall;
-
-
-#endif
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-2014
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -14,22 +14,22 @@
 
 /* if no specific type can be identified */
 static lookups generic[] = {
-	{ "TI-", "TITLE" ,    TITLE,  LEVEL_MAIN },
-	{ "AU-", "AUTHOR",    PERSON, LEVEL_MAIN },
-	{ "MV-", "VOLUME",    SIMPLE, LEVEL_MAIN },
-	{ "SE-", "TITLE",     TITLE,  LEVEL_HOST },
-	{ "ED-", "EDITION",   SIMPLE, LEVEL_MAIN },
-	{ "SC-", "SCALE",     SIMPLE, LEVEL_MAIN }, /* for maps */
-	{ "PU-", "PUBLISHER", SIMPLE, LEVEL_MAIN },
-	{ "PY-", "YEAR",      SIMPLE, LEVEL_MAIN },
-	{ "PD-", "DESCRIPTION",SIMPLE, LEVEL_MAIN }, /* physical description */
-	{ "DT-", "TYPE",      SIMPLE, LEVEL_MAIN },
-	{ "LA-", "LANGUAGE",  SIMPLE, LEVEL_MAIN },
+	{ "TI-", "TITLE" ,       TITLE,  LEVEL_MAIN },
+	{ "AU-", "AUTHOR",       PERSON, LEVEL_MAIN },
+	{ "MV-", "VOLUME",       SIMPLE, LEVEL_MAIN },
+	{ "SE-", "TITLE",        TITLE,  LEVEL_HOST },
+	{ "ED-", "EDITION",      SIMPLE, LEVEL_MAIN },
+	{ "SC-", "SCALE",        SIMPLE, LEVEL_MAIN }, /* for maps */
+	{ "PU-", "PUBLISHER",    SIMPLE, LEVEL_MAIN },
+	{ "PY-", "DATE:YEAR",    SIMPLE, LEVEL_MAIN },
+	{ "PD-", "DESCRIPTION",  SIMPLE, LEVEL_MAIN }, /* physical description */
+	{ "DT-", "TYPE",         SIMPLE, LEVEL_MAIN },
+	{ "LA-", "LANGUAGE",     SIMPLE, LEVEL_MAIN },
 	{ "IS-", "SERIALNUMBER", SERIALNO, LEVEL_MAIN },
-	{ "NT-", "NOTES",     SIMPLE, LEVEL_MAIN },
-	{ "KW-", "KEYWORD",   SIMPLE, LEVEL_MAIN },
-	{ "UL-", "URL",       SIMPLE, LEVEL_MAIN },
-	{ "HL-", "LOCATION",  SIMPLE, LEVEL_MAIN }
+	{ "NT-", "NOTES",        NOTES,  LEVEL_MAIN },
+	{ "KW-", "KEYWORD",      SIMPLE, LEVEL_MAIN },
+	{ "UL-", "URL",          SIMPLE, LEVEL_MAIN },
+	{ "HL-", "LOCATION",     SIMPLE, LEVEL_MAIN }
 };
 
 /* order is important....."Book" matches "Book" and "Book Section", hence
diff --git a/bibutils/doi.c b/bibutils/doi.c
deleted file mode 100644
--- a/bibutils/doi.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * doi.c
- *
- * doi_to_url()
- * Handle outputing DOI as a URL (Endnote and RIS formats)
- *     1) Append http://dx.doi.org as necessary
- *     2) Check for overlap with pre-existing URL for the DOI
- *
- * is_doi()
- * Check for DOI buried in another field.
- *
- * Copyright (c) Chris Putnam 2008-2013
- *
- * Source code released under the GPL version 2
- *
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include "newstr.h"
-#include "fields.h"
-
-static void
-construct_url( char *prefix, newstr *id, newstr *id_url )
-{
-	if ( !strncasecmp( id->data, "http:", 5 ) )
-		newstr_newstrcpy( id_url, id );
-	else {
-		newstr_strcpy( id_url, prefix );
-		if ( id->data[0]!='/' ) newstr_addchar( id_url, '/' );
-		newstr_newstrcat( id_url, id );
-	}
-}
-
-static int
-url_exists( fields *f, char *urltag, newstr *doi_url )
-{
-	int i, n;
-	if ( urltag ) {
-		n = fields_num( f );
-		for ( i=0; i<n; ++i ) {
-			if ( strcmp( fields_tag( f, i, FIELDS_CHRP ), urltag ) ) continue;
-			if ( strcmp( fields_value( f, i, FIELDS_CHRP ), doi_url->data ) ) continue;
-			return 1;
-		}
-	}
-	return 0;
-}
-
-static void
-xxx_to_url( fields *f, int n, char *http_prefix, char *urltag, newstr *xxx_url )
-{
-	newstr_empty( xxx_url );
-	construct_url( http_prefix, fields_value( f, n, FIELDS_STRP ), xxx_url );
-	if ( url_exists( f, urltag, xxx_url ) )
-		newstr_empty( xxx_url );
-}
-void
-doi_to_url( fields *f, int n, char *urltag, newstr *url )
-{
-	xxx_to_url( f, n, "http://dx.doi.org", urltag, url );
-}
-void
-jstor_to_url( fields *f, int n, char *urltag, newstr *url )
-{
-	xxx_to_url( f, n, "http://www.jstor.org/stable", urltag, url );
-}
-void
-pmid_to_url( fields *f, int n, char *urltag, newstr *url )
-{
-	xxx_to_url( f, n, "http://www.ncbi.nlm.nih.gov/pubmed", urltag, url );
-}
-void
-arxiv_to_url( fields *f, int n, char *urltag, newstr *url )
-{
-	xxx_to_url( f, n, "http://arxiv.org/abs", urltag, url );
-}
-
-/* Rules for the pattern:
- *   '#' = number
- *   isalpha() = match precisely (matchcase==1) or match regardless of case
- *   	(matchcase==0)
- *   all others must match precisely
- */
-static int
-string_pattern( char *s, char *pattern, int matchcase )
-{
-	int patlen, match, i;
-	patlen = strlen( pattern );
-	if ( strlen( s ) < patlen ) return 0; /* too short */
-	for ( i=0; i<patlen; ++i ) {
-		match = 0;
-		if ( pattern[i]=='#' ) {
-			if ( isdigit( (unsigned char)s[i] ) ) match = 1;
-		} else if ( !matchcase && isalpha( (unsigned char)pattern[i] ) ) {
-			if ( tolower((unsigned char)pattern[i])==tolower((unsigned char)s[i])) match = 1;
-		} else {
-			if ( pattern[i] == s[i] ) match = 1;
-		}
-		if ( !match ) return 0;
-	}
-	return 1;
-}
-
-/* science direct is now doing "M3  - doi: DOI: 10.xxxx/xxxxx" */
-int
-is_doi( char *s )
-{
-	if ( string_pattern( s, "##.####/", 0 ) ) return 0;
-	if ( string_pattern( s, "doi:##.####/", 0 ) ) return 4;
-	if ( string_pattern( s, "doi: ##.####/", 0 ) ) return 5;
-	if ( string_pattern( s, "doi: DOI: ##.####/", 0 ) ) return 10;
-	return -1;
-}
diff --git a/bibutils/doi.h b/bibutils/doi.h
deleted file mode 100644
--- a/bibutils/doi.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * doi.h
- *
- * Copyright (c) Chris Putnam 2004-2013
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef DOI_H
-#define DOI_H
-
-#include "newstr.h"
-#include "fields.h"
-
-extern void doi_to_url( fields *info, int n, char *urltag, newstr *doi_url );
-extern int is_doi( char *s );
-extern void pmid_to_url( fields *info, int n, char *urltag, newstr *pmid_url );
-extern void arxiv_to_url( fields *info, int n, char *urltag, newstr *arxiv_url );
-extern void jstor_to_url( fields *info, int n, char *urltag, newstr *jstor_url );
-
-#endif
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-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -9,13 +9,17 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "is_ws.h"
-#include "newstr.h"
-#include "newstr_conv.h"
+#include "str.h"
+#include "str_conv.h"
 #include "fields.h"
 #include "xml.h"
 #include "xml_encoding.h"
-#include "ebiin.h"
+#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 );
+
+
 /*****************************************************
  PUBLIC: void ebiin_initparams()
 *****************************************************/
@@ -42,8 +46,8 @@
 	p->all      = NULL;
 	p->nall     = 0;
 
-	list_init( &(p->asis) );
-	list_init( &(p->corps) );
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
 
 	if ( !progname ) p->progname = NULL;
 	else p->progname = strdup( progname );
@@ -52,14 +56,14 @@
 /*****************************************************
  PUBLIC: int ebiin_readf()
 *****************************************************/
-int
-ebiin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset )
+static int
+ebiin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
-	newstr tmp;
-	char *startptr = NULL, *endptr;
 	int haveref = 0, inref = 0, file_charset = CHARSET_UNKNOWN, m;
-	newstr_init( &tmp );
-	while ( !haveref && newstr_fget( fp, buf, bufsize, bufpos, line ) ) {
+	char *startptr = NULL, *endptr;
+	str tmp;
+	str_init( &tmp );
+	while ( !haveref && str_fget( fp, buf, bufsize, bufpos, line ) ) {
 		if ( line->data ) {
 			m = xml_getencoding( line );
 			if ( m!=CHARSET_UNKNOWN ) file_charset = m;
@@ -67,19 +71,19 @@
 		if ( line->data )
 			startptr = xml_findstart( line->data, "Publication" );
 		if ( startptr || inref ) {
-			if ( inref ) newstr_strcat( &tmp, line->data );
+			if ( inref ) str_strcat( &tmp, line );
 			else {
-				newstr_strcat( &tmp, startptr );
+				str_strcatc( &tmp, startptr );
 				inref = 1;
 			}
-			endptr = xml_findend( tmp.data, "Publication" );
+			endptr = xml_findend( str_cstr( &tmp ), "Publication" );
 			if ( endptr ) {
-				newstr_segcpy( reference, tmp.data, endptr );
+				str_segcpy( reference, str_cstr( &tmp ), endptr );
 				haveref = 1;
 			}
 		}
 	}
-	newstr_free( &tmp );
+	str_free( &tmp );
 	*fcharset = file_charset;
 	return haveref;
 }
@@ -101,9 +105,9 @@
 	int i, status;
 	char *d;
 
-	if ( !xml_hasdata( node ) ) goto out;
+	if ( !xml_hasvalue( node ) ) goto out;
 
-	d = xml_data( node );
+	d = xml_value( node );
 	for ( i=0; i<nc; ++i ) {
 		if ( c[i].a==NULL ) {
 			if ( xml_tagexact( node, c[i].in ) ) {
@@ -135,8 +139,8 @@
 ebiin_title( xml *node, fields *info, int title_level )
 {
 	int status;
-	if ( xml_hasdata( node ) ) {
-		status = fields_add( info, "TITLE", xml_data( node ), title_level );
+	if ( xml_hasvalue( node ) ) {
+		status = fields_add( info, "TITLE", xml_value( node ), title_level );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	return BIBL_OK;
@@ -148,38 +152,38 @@
  *             <MedlineDate>2003 Jan-Feb</MedlineDate>
  */
 static int
-ebiin_medlinedate_year( fields *info, char *p, newstr *s, int level, char **end )
+ebiin_medlinedate_year( fields *info, char *p, str *s, int level, char **end )
 {
 	int status;
-	*end = newstr_cpytodelim( s, p, " \t\n\r", 0 );
-	if ( newstr_memerr( s ) ) return BIBL_ERR_MEMERR;
-	if ( s->len ) {
-		status = fields_add( info, "PARTYEAR", s->data, level );
+	*end = str_cpytodelim( s, p, " \t\n\r", 0 );
+	if ( str_memerr( s ) ) return BIBL_ERR_MEMERR;
+	if ( str_has_value( s ) ) {
+		status = fields_add( info, "PARTDATE:YEAR", s->data, level );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	return BIBL_OK;
 }
 static int
-ebiin_medlinedate_month( fields *info, char *p, newstr *s, int level, char **end )
+ebiin_medlinedate_month( fields *info, char *p, str *s, int level, char **end )
 {
 	int status;
-	*end = newstr_cpytodelim( s, p, " \t\n\r", 0 );
-	newstr_findreplace( s, "-", "/" );
-	if ( newstr_memerr( s ) ) return BIBL_ERR_MEMERR;
-	if ( s->len ) {
-		status = fields_add( info, "PARTMONTH", s->data, level );
+	*end = str_cpytodelim( s, p, " \t\n\r", 0 );
+	str_findreplace( s, "-", "/" );
+	if ( str_memerr( s ) ) return BIBL_ERR_MEMERR;
+	if ( str_has_value( s ) ) {
+		status = fields_add( info, "PARTDATE:MONTH", s->data, level );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	return BIBL_OK;
 }
 static int
-ebiin_medlinedate_day( fields *info, char *p, newstr *s, int level, char **end )
+ebiin_medlinedate_day( fields *info, char *p, str *s, int level, char **end )
 {
 	int status;
-	*end = newstr_cpytodelim( s, p, " \t\n\r", 0 );
-	if ( newstr_memerr( s ) ) return BIBL_ERR_MEMERR;
-	if ( s->len ) {
-		status = fields_add( info, "PARTDAY", s->data, level );
+	*end = str_cpytodelim( s, p, " \t\n\r", 0 );
+	if ( str_memerr( s ) ) return BIBL_ERR_MEMERR;
+	if ( str_has_value( s ) ) {
+		status = fields_add( info, "PARTDATE:DAY", s->data, level );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	return BIBL_OK;
@@ -188,14 +192,14 @@
 ebiin_medlinedate( fields *info, char *p, int level )
 {
 	int status;
-	newstr s;
-	newstr_init( &s );
+	str s;
+	str_init( &s );
 	status = ebiin_medlinedate_year( info, skip_ws( p ), &s, level, &p );
 	if ( status==BIBL_OK && *p )
 		status = ebiin_medlinedate_month( info, skip_ws( p ), &s, level, &p );
 	if ( status==BIBL_OK && *p )
 		status = ebiin_medlinedate_day( info, skip_ws( p ), &s, level, &p );
-	newstr_free( &s );
+	str_free( &s );
 	return status;
 }
 
@@ -231,21 +235,21 @@
 ebiin_journal1( xml *node, fields *info )
 {
 	xml_convert c[] = {
-		{ "ISSN",     NULL, NULL, "ISSN",      1 },
-		{ "Volume",   NULL, NULL, "VOLUME",    1 },
-		{ "Issue",    NULL, NULL, "ISSUE",     1 },
-		{ "Year",     NULL, NULL, "PARTYEAR",  1 },
-		{ "Month",    NULL, NULL, "PARTMONTH", 1 },
-		{ "Day",      NULL, NULL, "PARTDAY",   1 },
-		{ "Language", NULL, NULL, "LANGUAGE",  1 },
+		{ "ISSN",     NULL, NULL, "ISSN",           1 },
+		{ "Volume",   NULL, NULL, "VOLUME",         1 },
+		{ "Issue",    NULL, NULL, "ISSUE",          1 },
+		{ "Year",     NULL, NULL, "PARTDATE:YEAR",  1 },
+		{ "Month",    NULL, NULL, "PARTDATE:MONTH", 1 },
+		{ "Day",      NULL, NULL, "PARTDATE:DAY",   1 },
+		{ "Language", NULL, NULL, "LANGUAGE",       1 },
 	};
 	int nc = sizeof( c ) / sizeof( c[0] ), status, found;
-	if ( xml_hasdata( node ) ) {
+	if ( xml_hasvalue( node ) ) {
 		status = ebiin_doconvert( node, info, c, nc, &found );
 		if ( status!=BIBL_OK ) return status;
 		if ( !found ) {
 			if ( xml_tagexact( node, "MedlineDate" ) ) {
-				status = ebiin_medlinedate( info, xml_data( node ), 1 );
+				status = ebiin_medlinedate( info, xml_value( node ), 1 );
 				if ( status!=BIBL_OK ) return status;
 			}
 		}
@@ -270,26 +274,26 @@
 {
 	int i, status, ret = BIBL_OK;
 	const int level = 1;
-	newstr sp, ep, *up;
+	str sp, ep, *up;
 
-	newstrs_init( &sp, &ep, NULL );
+	strs_init( &sp, &ep, NULL );
 
 	/* ...start page */
-	p = newstr_cpytodelim( &sp, skip_ws( p ), "-", 1 );
-	if ( newstr_memerr( &sp ) ) {
+	p = str_cpytodelim( &sp, skip_ws( p ), "-", 1 );
+	if ( str_memerr( &sp ) ) {
 		ret = BIBL_ERR_MEMERR;
 		goto out;
 	}
 
 	/* ...end page */
-	p = newstr_cpytodelim( &ep, skip_ws( p ), " \t\n\r", 0 );
-	if ( newstr_memerr( &ep ) ) {
+	p = str_cpytodelim( &ep, skip_ws( p ), " \t\n\r", 0 );
+	if ( str_memerr( &ep ) ) {
 		ret = BIBL_ERR_MEMERR;
 		goto out;
 	}
 
 	if ( sp.len ) {
-		status = fields_add( info, "PAGESTART", sp.data, level );
+		status = fields_add( info, "PAGES:START", sp.data, level );
 		if ( status!=FIELDS_OK ) {
 			ret = BIBL_ERR_MEMERR;
 			goto out;
@@ -301,12 +305,12 @@
 				sp.data[i] = ep.data[i-sp.len+ep.len];
 				up = &(sp);
 		} else up = &(ep);
-		status = fields_add( info, "PAGEEND", up->data, level );
+		status = fields_add( info, "PAGES:STOP", up->data, level );
 		if ( status!=FIELDS_OK ) ret = BIBL_ERR_MEMERR;
 	}
 
 out:
-	newstrs_free( &sp, &ep, NULL );
+	strs_free( &sp, &ep, NULL );
 	return ret;
 }
 static int
@@ -314,7 +318,7 @@
 {
 	int status;
 	if ( xml_tagexact( node, "Pages" ) && node->value ) {
-		status = ebiin_pages( info, xml_data( node ) );
+		status = ebiin_pages( info, xml_value( node ) );
 		if ( status!=BIBL_OK ) return status;
 	}
 	if ( node->down ) {
@@ -336,8 +340,8 @@
 ebiin_abstract( xml *node, fields *info )
 {
 	int status;
-	if ( xml_hasdata( node ) && xml_tagexact( node, "AbstractText" ) ) {
-		status = fields_add( info, "ABSTRACT", xml_data( node ), 0 );
+	if ( xml_hasvalue( node ) && xml_tagexact( node, "AbstractText" ) ) {
+		status = fields_add( info, "ABSTRACT", xml_value( node ), 0 );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	else if ( node->next ) {
@@ -357,32 +361,32 @@
  * </AuthorList>
  */
 static int
-ebiin_author( xml *node, newstr *name )
+ebiin_author( xml *node, str *name )
 {
 	int status;
 	char *p;
 	if ( xml_tagexact( node, "LastName" ) ) {
 		if ( name->len ) {
-			newstr_prepend( name, "|" );
-			newstr_prepend( name, xml_data( node ) );
+			str_prepend( name, "|" );
+			str_prepend( name, xml_value( node ) );
 		}
-		else newstr_strcat( name, xml_data( node ) );
+		else str_strcatc( name, xml_value( node ) );
 	} else if ( xml_tagexact( node, "ForeName" ) || 
 	            xml_tagexact( node, "FirstName" ) ) {
-		p = xml_data( node );
+		p = xml_value( node );
 		while ( p && *p ) {
-			if ( name->len ) newstr_addchar( name, '|' );
+			if ( name->len ) str_addchar( name, '|' );
 			while ( *p && *p==' ' ) p++;
-			while ( *p && *p!=' ' ) newstr_addchar( name, *p++ );
+			while ( *p && *p!=' ' ) str_addchar( name, *p++ );
 		}
 	} else if ( xml_tagexact( node, "Initials" ) && !strchr( name->data, '|' ) ) {
-		p = xml_data( node );
+		p = xml_value( node );
 		while ( p && *p ) {
-			if ( name->len ) newstr_addchar( name, '|' );
-			if ( !is_ws(*p ) ) newstr_addchar( name, *p++ );
+			if ( name->len ) str_addchar( name, '|' );
+			if ( !is_ws(*p ) ) str_addchar( name, *p++ );
 		}
 	}
-	if ( newstr_memerr( name ) ) return BIBL_ERR_MEMERR;
+	if ( str_memerr( name ) ) return BIBL_ERR_MEMERR;
 		 
 	if ( node->down ) {
 		status = ebiin_author( node->down, name );
@@ -399,9 +403,9 @@
 ebiin_authorlist( xml *node, fields *info, int level )
 {
 	int fstatus, status = BIBL_OK;
-	newstr name;
+	str name;
 
-	newstr_init( &name );
+	str_init( &name );
 	node = node->down;
 	while ( node ) {
 		if ( xml_tagexact( node, "Author" ) && node->down ) {
@@ -410,13 +414,13 @@
 			if ( name.len ) {
 				fstatus = fields_add(info,"AUTHOR",name.data,level);
 				if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-				newstr_empty( &name );
+				str_empty( &name );
 			}
 		}
 		node = node->next;
 	}
 out:
-	newstr_free( &name );
+	str_free( &name );
 	return status;
 }
 
@@ -436,8 +440,8 @@
 ebiin_journal2( xml *node, fields *info )
 {
 	int status;
-	if ( xml_tagwithdata( node, "TitleAbbreviation" ) ) {
-		status = fields_add( info, "TITLE", xml_data( node ), 1 );
+	if ( xml_tagwithvalue( node, "TitleAbbreviation" ) ) {
+		status = fields_add( info, "TITLE", xml_value( node ), 1 );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	if ( node->down ) {
@@ -465,8 +469,8 @@
 ebiin_meshheading( xml *node, fields *info )
 {
 	int status;
-	if ( xml_tagwithdata( node, "DescriptorName" ) ) {
-		status = fields_add( info, "KEYWORD", xml_data( node ), 0 );
+	if ( xml_tagwithvalue( node, "DescriptorName" ) ) {
+		status = fields_add( info, "KEYWORD", xml_value( node ), 0 );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	if ( node->next ) {
@@ -495,46 +499,46 @@
 ebiin_book( xml *node, fields *info, int book_level )
 {
 	xml_convert book[] = {
-		{ "Publisher",              NULL, NULL, "PUBLISHER",  0 },
-		{ "Language",               NULL, NULL, "LANGUAGE",   0 },
-		{ "ISBN10",                 NULL, NULL, "ISBN",       0 },
-		{ "ISBN13",                 NULL, NULL, "ISBN13",     0 },
-		{ "Year",                   NULL, NULL, "YEAR",       0 },
-		{ "Month",                  NULL, NULL, "MONTH",      0 },
-		{ "Day",                    NULL, NULL, "DAY",        0 },
-		{ "PageTotal",              NULL, NULL, "TOTALPAGES", 0 },
-		{ "SeriesName",             NULL, NULL, "TITLE",      1 },
-		{ "SeriesISSN",             NULL, NULL, "ISSN",       0 },
-		{ "OtherReportInformation", NULL, NULL, "NOTES",      0 },
-		{ "Edition",                NULL, NULL, "EDITION",    0 },
+		{ "Publisher",              NULL, NULL, "PUBLISHER",      0 },
+		{ "Language",               NULL, NULL, "LANGUAGE",       0 },
+		{ "ISBN10",                 NULL, NULL, "ISBN",           0 },
+		{ "ISBN13",                 NULL, NULL, "ISBN13",         0 },
+		{ "Year",                   NULL, NULL, "DATE:YEAR",      0 },
+		{ "Month",                  NULL, NULL, "DATE:MONTH",     0 },
+		{ "Day",                    NULL, NULL, "DATE:DAY",       0 },
+		{ "PageTotal",              NULL, NULL, "PAGES:TOTAL",    0 },
+		{ "SeriesName",             NULL, NULL, "TITLE",          1 },
+		{ "SeriesISSN",             NULL, NULL, "ISSN",           0 },
+		{ "OtherReportInformation", NULL, NULL, "NOTES",          0 },
+		{ "Edition",                NULL, NULL, "EDITION",        0 },
 	};
 	int nbook = sizeof( book ) / sizeof( book[0] );
 	xml_convert inbook[] = {
-		{ "Publisher",              NULL, NULL, "PUBLISHER",  1 },
-		{ "Language",               NULL, NULL, "LANGUAGE",   0 },
-		{ "ISBN10",                 NULL, NULL, "ISBN",       1 },
-		{ "ISBN13",                 NULL, NULL, "ISBN13",     1 },
-		{ "Year",                   NULL, NULL, "PARTYEAR",   1 },
-		{ "Month",                  NULL, NULL, "PARTMONTH",  1 },
-		{ "Day",                    NULL, NULL, "PARTDAY",    1 },
-		{ "PageTotal",              NULL, NULL, "TOTALPAGES", 1 },
-		{ "SeriesName",             NULL, NULL, "TITLE",      2 },
-		{ "SeriesISSN",             NULL, NULL, "ISSN",       1 },
-		{ "OtherReportInformation", NULL, NULL, "NOTES",      1 },
-		{ "Edition",                NULL, NULL, "EDITION",    1 },
+		{ "Publisher",              NULL, NULL, "PUBLISHER",      1 },
+		{ "Language",               NULL, NULL, "LANGUAGE",       0 },
+		{ "ISBN10",                 NULL, NULL, "ISBN",           1 },
+		{ "ISBN13",                 NULL, NULL, "ISBN13",         1 },
+		{ "Year",                   NULL, NULL, "PARTDATE:YEAR",  1 },
+		{ "Month",                  NULL, NULL, "PARTDATE:MONTH", 1 },
+		{ "Day",                    NULL, NULL, "PARTDATE:DAY",   1 },
+		{ "PageTotal",              NULL, NULL, "PAGES:TOTAL",    1 },
+		{ "SeriesName",             NULL, NULL, "TITLE",          2 },
+		{ "SeriesISSN",             NULL, NULL, "ISSN",           1 },
+		{ "OtherReportInformation", NULL, NULL, "NOTES",          1 },
+		{ "Edition",                NULL, NULL, "EDITION",        1 },
 	};
 	int ninbook = sizeof( inbook ) / sizeof( inbook[0] );
 	xml_convert *c;
 	int nc, status, found;
 	if ( book_level==0 ) { c = book; nc = nbook; }
 	else { c = inbook; nc = ninbook; }
-	if ( xml_hasdata( node ) ) {
+	if ( xml_hasvalue( node ) ) {
 		status = ebiin_doconvert( node, info, c, nc, &found );
 		if ( status!=BIBL_OK ) return status;
 		if ( !found ) {
 			status = BIBL_OK;
 			if ( xml_tagexact( node, "MedlineDate" ) )
-				status = ebiin_medlinedate( info, xml_data( node ), book_level);
+				status = ebiin_medlinedate( info, xml_value( node ), book_level);
 			else if ( xml_tagexact( node, "Title" ) )
 				status = ebiin_title( node, info, book_level );
 			else if ( xml_tagexact( node, "Pagination" ) && node->down )
@@ -586,7 +590,7 @@
 static int
 ebiin_publication( xml *node, fields *info )
 {
-	int status;
+	int status = BIBL_OK;
 	if ( node->down ) {
 		if ( xml_tagexact( node, "Article" ) )
 			status = ebiin_article( node->down, info );
@@ -612,7 +616,7 @@
 ebiin_fixtype( xml *node, fields *info )
 {
 	char *resource = NULL, *issuance = NULL, *genre1 = NULL, *genre2 = NULL;
-	newstr *type;
+	str *type;
 	int reslvl, isslvl, gen1lvl, gen2lvl;
 	int status;
 
@@ -684,8 +688,8 @@
 	return BIBL_OK;
 }
 
-int
-ebiin_processf( fields *ebiin, char *data, char *filename, long nref )
+static int
+ebiin_processf( fields *ebiin, char *data, char *filename, long nref, param *p )
 {
 	int status;
 	xml top;
diff --git a/bibutils/ebiin.h b/bibutils/ebiin.h
deleted file mode 100644
--- a/bibutils/ebiin.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * ebiin.h
- *
- * Copyright (c) Chris Putnam 2004-2013
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef PUBIN_H
-#define PUBIN_H
-
-#include "newstr.h"
-#include "fields.h"
-#include "reftypes.h"
-#include "bibutils.h"
-
-extern int ebiin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset );
-extern int ebiin_processf( fields *ebiin, char *data, char *filename, long nref );
-
-extern void ebiin_initparams( param *p, const char *progname );
-
-
-extern variants med_all[];
-extern int med_nall;
-
-#endif
-
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-2014
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -11,19 +11,27 @@
 #include <string.h>
 #include <ctype.h>
 #include "is_ws.h"
-#include "doi.h"
-#include "newstr.h"
-#include "newstr_conv.h"
+#include "str.h"
+#include "str_conv.h"
 #include "fields.h"
-#include "name.h"
-#include "title.h"
-#include "serialno.h"
+#include "url.h"
 #include "reftypes.h"
-#include "endin.h"
+#include "bibformats.h"
+#include "generic.h"
 
+extern variants end_all[];
+extern int end_nall;
+
 /*****************************************************
  PUBLIC: void endin_initparams()
 *****************************************************/
+
+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 );
+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 )
 {
@@ -46,8 +54,8 @@
 	p->all      = end_all;
 	p->nall     = end_nall;
 
-	list_init( &(p->asis) );
-	list_init( &(p->corps) );
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
 
 	if ( !progname ) p->progname = NULL;
 	else p->progname = strdup( progname );
@@ -76,14 +84,14 @@
 }
 
 static int
-readmore( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line )
+readmore( FILE *fp, char *buf, int bufsize, int *bufpos, str *line )
 {
 	if ( line->len ) return 1;
-	else return newstr_fget( fp, buf, bufsize, bufpos, line );
+	else return str_fget( fp, buf, bufsize, bufpos, line );
 }
 
-int
-endin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset )
+static int
+endin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
 	int haveref = 0, inref = 0;
 	unsigned char *up;
@@ -108,14 +116,14 @@
 		}
 		/* Each reference starts with a tag && ends with a blank line */
 		if ( endin_istag( p ) ) {
-			if ( reference->len ) newstr_addchar( reference, '\n' );
-			newstr_strcat( reference, p );
+			if ( reference->len ) str_addchar( reference, '\n' );
+			str_strcatc( reference, p );
 			inref = 1;
 		} else if ( inref && p ) {
-			newstr_addchar( reference, '\n' );
-		   	newstr_strcat( reference, p );
+			str_addchar( reference, '\n' );
+			str_strcatc( reference, p );
 		}
-		newstr_empty( line );
+		str_empty( line );
 	}
 	if ( reference->len ) haveref = 1;
 	return haveref;
@@ -125,20 +133,20 @@
  PUBLIC: int endin_processf()
 *****************************************************/
 static char*
-process_endline( newstr *tag, newstr *data, char *p )
+process_endline( str *tag, str *data, char *p )
 {
 	int  i;
 
 	i = 0;
 	while ( i<2 && *p ) {
-		newstr_addchar( tag, *p++);
+		str_addchar( tag, *p++);
 		i++;
 	}
 	while ( *p==' ' || *p=='\t' ) p++;
 
 	while ( *p && *p!='\r' && *p!='\n' )
-		newstr_addchar( data, *p++ );
-	newstr_trimendingws( data );
+		str_addchar( data, *p++ );
+	str_trimendingws( data );
 
 	while ( *p=='\r' || *p=='\n' ) p++;
 
@@ -146,45 +154,45 @@
 }
 
 static char *
-process_endline2( newstr *tag, newstr *data, char *p )
+process_endline2( str *tag, str *data, char *p )
 {
 	while ( *p==' ' || *p=='\t' ) p++;
 	while ( *p && *p!='\r' && *p!='\n' )
-		newstr_addchar( data, *p++ );
-	newstr_trimendingws( data );
+		str_addchar( data, *p++ );
+	str_trimendingws( data );
 	while ( *p=='\r' || *p=='\n' ) p++;
 	return p;
 }
 
-int
-endin_processf( fields *endin, char *p, char *filename, long nref )
+static int
+endin_processf( fields *endin, char *p, char *filename, long nref, param *pm )
 {
-	newstr tag, data;
+	str tag, data;
 	int status, n;
-	newstrs_init( &tag, &data, NULL );
+	strs_init( &tag, &data, NULL );
 	while ( *p ) {
-		newstrs_empty( &tag, &data, NULL );
+		strs_empty( &tag, &data, NULL );
 		if ( endin_istag( p ) ) {
 			p = process_endline( &tag, &data, p );
-			if ( data.len==0 ) continue;
-			status = fields_add( endin, tag.data, data.data, 0 );
+			if ( str_is_empty( &data ) ) continue;
+			status = fields_add( endin, str_cstr( &tag ), str_cstr( &data ), 0 );
 			if ( status!=FIELDS_OK ) return 0;
 		} else {
 			p = process_endline2( &tag, &data, p );
 			/* endnote puts %K only on 1st line of keywords */
 			n = fields_num( endin );
-			if ( n>0 && data.len ) {
+			if ( n>0 && str_has_value( &data ) ) {
 			if ( !strncmp( endin->tag[n-1].data, "%K", 2 ) ) {
-				status = fields_add( endin, "%K", data.data, 0 );
+				status = fields_add( endin, "%K", str_cstr( &data ), 0 );
 				if ( status!=FIELDS_OK ) return 0;
 			} else {
-				newstr_addchar( &(endin->data[n-1]), ' ' );
-				newstr_strcat( &(endin->data[n-1]), data.data );
+				str_addchar( &(endin->data[n-1]), ' ' );
+				str_strcat( &(endin->data[n-1]), &data );
 			}
 			}
 		}
 	}
-	newstrs_free( &tag, &data, NULL );
+	strs_free( &tag, &data, NULL );
 	return 1;
 }
 
@@ -192,7 +200,7 @@
  PUBLIC: int endin_typef()
 *****************************************************/
 
-/* Endnote defaults if no %0
+/* Endnote defaults if no %0 tag
  *
  * if %J & %V - journal article
  * if %B - book section
@@ -201,18 +209,16 @@
  * if !%B & !%J & !%R & !%I - journal article
  */
 int
-endin_typef( fields *endin, char *filename, int nrefs, param *p, 
-	variants *all, int nall )
+endin_typef( fields *endin, char *filename, int nrefs, param *p )
 {
-	char *refnum = "";
-	int n, reftype, nrefnum, nj, nv, nb, nr, nt, ni;
-	n = fields_find( endin, "%0", 0 );
-	nrefnum = fields_find( endin, "%F", 0 );
-	if ( nrefnum!=-1 ) refnum = endin->data[nrefnum].data;
-	if ( n!=-1 ) {
-		reftype = get_reftype( endin->data[n].data, nrefs, 
-			p->progname, all, nall, refnum );
-	} else {
+	int ntypename, nrefname, is_default, nj, nv, nb, nr, nt, ni;
+	char *refname = "", *typename="";
+
+	ntypename = fields_find( endin, "%0", LEVEL_MAIN );
+	nrefname  = fields_find( endin, "%F", LEVEL_MAIN );
+	if ( nrefname!=-1  ) refname  = fields_value( endin, nrefname,  FIELDS_CHRP_NOUSE );
+	if ( ntypename!=-1 ) typename = fields_value( endin, ntypename, FIELDS_CHRP_NOUSE );
+	else {
 		nj = fields_find( endin, "%J", 0 );
 		nv = fields_find( endin, "%V", 0 );
 		nb = fields_find( endin, "%B", 0 );
@@ -220,26 +226,19 @@
 		nt = fields_find( endin, "%T", 0 );
 		ni = fields_find( endin, "%I", 0 );
 		if ( nj!=-1 && nv!=-1 ) {
-			reftype = get_reftype( "Journal Article", nrefs,
-					p->progname, all, nall, refnum );
+			typename = "Journal Article";
 		} else if ( nb!=-1 ) {
-			reftype = get_reftype( "Book Section", nrefs,
-					p->progname, all, nall, refnum );
+			typename = "Book Section";
 		} else if ( nr!=-1 && nt==-1 ) {
-			reftype = get_reftype( "Report", nrefs,
-					p->progname, all, nall, refnum );
+			typename = "Report";
 		} else if ( ni!=-1 && nb==-1 && nj==-1 && nr==-1 ) {
-			reftype = get_reftype( "Book", nrefs,
-					p->progname, all, nall, refnum );
+			typename = "Book";
 		} else if ( nb==-1 && nj==-1 && nr==-1 && ni==-1 ) {
-			reftype = get_reftype( "Journal Article", nrefs,
-					p->progname, all, nall, refnum );
-		} else {
-			reftype = get_reftype( "", nrefs, p->progname, 
-					all, nall, refnum ); /* default */
+			typename = "Journal Article";
 		}
 	}
-	return reftype;
+
+	return get_reftype( typename, nrefs, p->progname, p->all, p->nall, refname, &is_default, REFTYPE_CHATTY );
 }
 
 /*****************************************************
@@ -247,43 +246,44 @@
 *****************************************************/
 
 /* Wiley puts multiple authors separated by commas on the %A lines.
- * We can detect this by finding the terminal comma in the data.
+ * We can detect this by finding the terminal comma in the value
+ * from the tag/value pair.
  *
  * "%A" "Author A. X. Last, Author N. B. Next,"
  */
 static int
 is_wiley_author( fields *endin, int n )
 {
-	newstr *t, *d;
-	t = &(endin->tag[n]);
-	if ( !t->data || strcmp( t->data, "%A" ) ) return 0;
-	d = &( endin->data[n] );
-	if ( !(d->data) || d->len==0 ) return 0;
-	if ( d->data[d->len-1]!=',' ) return 0;
+	str *t, *v;
+	t = fields_tag( endin, n, FIELDS_STRP_NOUSE );
+	if ( str_is_empty( t ) || strcmp( t->data, "%A" ) ) return 0;
+	v = fields_value( endin, n, FIELDS_STRP_NOUSE );
+	if ( str_is_empty( v ) ) return 0;
+	if ( v->data[v->len-1]!=',' ) return 0;
 	return 1;
 }
 
 static int
 cleanup_wiley_author( fields *endin, int n )
 {	
-	newstr *instring, copy, name;
+	str *instring, copy, name;
 	int status, nauthor = 0;
 	char *p;
 
-	newstrs_init( &copy, &name, NULL );
+	strs_init( &copy, &name, NULL );
 
 	instring = &( endin->data[n] );
-	newstr_newstrcpy( &copy, instring );
+	str_strcpy( &copy, instring );
 
 	p = copy.data;
 	while ( *p ) {
 		if ( *p==',' ) {
-			if ( newstr_memerr( &name ) )
+			if ( str_memerr( &name ) )
 				return BIBL_ERR_MEMERR;
 			if ( nauthor==0 ) {
 				/* ...replace the first author in the field */
-				newstr_newstrcpy( instring, &name );
-				if ( newstr_memerr( instring ) )
+				str_strcpy( instring, &name );
+				if ( str_memerr( instring ) )
 					return BIBL_ERR_MEMERR;
 			} else {
 				status = fields_add( endin, endin->tag[n].data,
@@ -291,17 +291,17 @@
 				if ( status!=FIELDS_OK )
 					return BIBL_ERR_MEMERR;
 			}
-			newstr_empty( &name );
+			str_empty( &name );
 			nauthor++;
 			p++;
 			while ( is_ws( *p ) ) p++;
 		} else {
-			newstr_addchar( &name, *p );
+			str_addchar( &name, *p );
 			p++;
 		}
 	}
 
-	newstrs_free( &copy, &name, NULL );
+	strs_free( &copy, &name, NULL );
 	return BIBL_OK;
 }
 
@@ -332,35 +332,6 @@
  PUBLIC: int endin_convertf(), returns BIBL_OK or BIBL_ERR_MEMERR
 *****************************************************/
 
-static int
-endin_addpage( fields *info, char *p, int level )
-{
-	newstr page;
-	int status;
-
-	newstr_init( &page );
-
-	p = newstr_cpytodelim( &page, skip_ws( p ), "- \t\r\n", 0 );
-	if ( newstr_memerr( &page ) ) return BIBL_ERR_MEMERR;
-	if ( page.len>0 ) {
-		status = fields_add( info, "PAGESTART", page.data, level );
-		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-	}
-
-	while ( *p && (is_ws(*p) || *p=='-' ) ) p++;
-
-	p = newstr_cpytodelim( &page, p, "- \t\r\n", 0 );
-	if ( newstr_memerr( &page ) ) return BIBL_ERR_MEMERR;
-	if ( page.len>0 ) {
-		status = fields_add( info, "PAGEEND", page.data, level );
-		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-	}
-
-	newstr_free( &page );
-
-	return BIBL_OK;
-}
-
 /* month_convert()
  * convert month name to number in format MM, e.g. "January" -> "01"
  * if converted, return 1
@@ -403,28 +374,29 @@
 }
 
 static int
-endin_adddate( fields *info, char *tag, char *newtag, char *p, int level )
+endin_date( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
 {
 	char *tags[3][2] = {
-		{ "YEAR",  "PARTYEAR" },
-		{ "MONTH", "PARTMONTH" },
-		{ "DAY",   "PARTDAY" }
+		{ "DATE:YEAR",  "PARTDATE:YEAR" },
+		{ "DATE:MONTH", "PARTDATE:MONTH" },
+		{ "DATE:DAY",   "PARTDATE:DAY" }
 	};
+	char *p = invalue->data;
 	char month[10], *m;
 	int part, status;
-	newstr date;
+	str date;
 
-	newstr_init( &date );
+	str_init( &date );
 
-	if ( !strncasecmp( newtag, "PART", 4 ) ) part = 1;
+	if ( !strncasecmp( outtag, "PART", 4 ) ) part = 1;
 	else part = 0;
 
 	/* %D YEAR */
-	if ( !strcasecmp( tag, "%D" ) ) {
-		newstr_cpytodelim( &date, skip_ws( p ), "", 0 );
-		if ( newstr_memerr( &date ) ) return BIBL_ERR_MEMERR;
-		if ( date.len>0 ) {
-			status = fields_add( info, tags[0][part], date.data, level );
+	if ( !strcasecmp( intag->data, "%D" ) ) {
+		str_cpytodelim( &date, skip_ws( p ), "", 0 );
+		if ( str_memerr( &date ) ) return BIBL_ERR_MEMERR;
+		if ( str_has_value( &date ) ) {
+			status = fields_add( bibout, tags[0][part], date.data, level );
 			if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 		}
 	}
@@ -432,15 +404,15 @@
 	/* %8 MONTH DAY, YEAR */
 	/* %8 MONTH, YEAR */
 	/* %8 MONTH YEAR */
-	else if ( !strcasecmp( tag, "%8" ) ) {
+	else if ( !strcasecmp( intag->data, "%8" ) ) {
 
 		/* ...get month */
-		p = newstr_cpytodelim( &date, skip_ws( p ), " ,\n", 0 );
-		if ( newstr_memerr( &date ) ) return BIBL_ERR_MEMERR;
-		if ( date.len>0 ) {
+		p = str_cpytodelim( &date, skip_ws( p ), " ,\n", 0 );
+		if ( str_memerr( &date ) ) return BIBL_ERR_MEMERR;
+		if ( str_has_value( &date ) ) {
 			if ( month_convert( date.data, month ) ) m = month;
-			else m = date.data;
-			status = fields_add( info, tags[1][part], m, level );
+			else m = str_cstr( &date );
+			status = fields_add( bibout, tags[1][part], m, level );
 			if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 		}
 
@@ -448,13 +420,13 @@
 		if ( *p==',' ) p++;
 
 		/* ...get days */
-		p = newstr_cpytodelim( &date, skip_ws( p ), ",\n", 0 );
-		if ( newstr_memerr( &date ) ) return BIBL_ERR_MEMERR;
+		p = str_cpytodelim( &date, skip_ws( p ), ",\n", 0 );
+		if ( str_memerr( &date ) ) return BIBL_ERR_MEMERR;
 		if ( date.len>0 && date.len<3 ) {
-			status = fields_add( info, tags[2][part], date.data, level );
+			status = fields_add( bibout, tags[2][part], date.data, level );
 			if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 		} else if ( date.len==4 ) {
-			status = fields_add( info, tags[0][part], date.data, level );
+			status = fields_add( bibout, tags[0][part], date.data, level );
 			if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 		}
 
@@ -462,19 +434,19 @@
 		if ( *p==',' ) p++;
 
 		/* ...get year */
-		p = newstr_cpytodelim( &date, skip_ws( p ), " \t\n\r", 0 );
-		if ( newstr_memerr( &date ) ) return BIBL_ERR_MEMERR;
-		if ( date.len > 0 ) {
-			status = fields_add( info, tags[0][part], date.data, level );
+		p = str_cpytodelim( &date, skip_ws( p ), " \t\n\r", 0 );
+		if ( str_memerr( &date ) ) return BIBL_ERR_MEMERR;
+		if ( str_has_value( &date ) ) {
+			status = fields_add( bibout, tags[0][part], date.data, level );
 			if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 		}
 	}
-	newstr_free( &date );
+	str_free( &date );
 	return BIBL_OK;
 }
 
 static int
-endin_addtype( fields *info, char *data, int level )
+endin_type( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
 {
 	lookups types[] = {
 		{ "GENERIC",                "ARTICLE" },
@@ -506,17 +478,16 @@
 	int  ntypes = sizeof( types ) / sizeof( lookups );
 	int  i, status, found=0;
 	for ( i=0; i<ntypes; ++i ) {
-		if ( !strcasecmp( types[i].oldstr, data ) ) {
+		if ( !strcasecmp( types[i].oldstr, invalue->data ) ) {
 			found = 1;
-			status = fields_add( info, "INTERNAL_TYPE", types[i].newstr, level );
+			status = fields_add( bibout, "INTERNAL_TYPE", types[i].newstr, level );
 			if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 		}
 	}
 	if ( !found ) {
-		fprintf( stderr, "Did not identify reference type '%s'\n",
-			data );
+		fprintf( stderr, "Did not identify reference type '%s'\n", invalue->data );
 		fprintf( stderr, "Defaulting to journal article type\n");
-		status = fields_add( info, "INTERNAL_TYPE", types[0].newstr, level );
+		status = fields_add( bibout, "INTERNAL_TYPE", types[0].newstr, level );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	return BIBL_OK;
@@ -531,110 +502,57 @@
 	}
 }
 
-/* Wiley's EndNote download has DOI's in "%1" tag */
-static int
-endin_addnotes( fields *info, char *tag, char *value, int level )
-{
-	int fstatus, doi;
-	doi = is_doi( value );
-	if ( doi!=-1 )
-		fstatus = fields_add( info, "DOI", &(value[doi]), level );
-	else
-		fstatus = fields_add( info, tag, value, level );
-	if ( fstatus==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
-
-static int
-endin_simple( fields *info, char *tag, char *value, int level )
-{
-	int fstatus = fields_add( info, tag, value, level );
-	if ( fstatus==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
-
 int
-endin_convertf( fields *endin, fields *info, int reftype, param *p, variants *all, int nall )
+endin_convertf( fields *bibin, fields *bibout, int reftype, param *p )
 {
-	int i, level, n, process, nfields, ok, fstatus, status = BIBL_OK;
-	char *newtag, *t;
-	newstr *d;
+	static int (*convertfns[NUM_REFTYPES])(fields *, int, str *, str *, int, param *, char *, fields *) = {
+		[ 0 ... NUM_REFTYPES-1 ] = generic_null,
+		[ SIMPLE       ] = generic_simple,
+		[ TITLE        ] = generic_title,
+		[ PERSON       ] = generic_person,
+		[ SERIALNO     ] = generic_serialno,
+		[ PAGES        ] = generic_pages,
+		[ NOTES        ] = generic_notes,
+		[ URL          ] = generic_url,
+		[ TYPE         ] = endin_type,
+		[ DATE         ] = endin_date,
+        };
 
-	nfields = fields_num( endin );
+	int i, level, process, nfields, fstatus, status = BIBL_OK;
+	char *outtag;
+	str *intag, *invalue;
+
+	nfields = fields_num( bibin );
 	for ( i=0; i<nfields; ++i ) {
-		/* Ensure that data exists */
-		d = fields_value( endin, i, FIELDS_STRP_NOUSE );
-		if ( d->len == 0 ) {
-			fields_setused( endin, i );
+
+		/* Ensure we have data */
+		if ( fields_nodata( bibin, i ) ) {
+			fields_setused( bibin, i );
 			continue;
 		}
+
+		intag = fields_tag( bibin, i, FIELDS_STRP );
+		invalue = fields_value( bibin, i, FIELDS_STRP );
+
 		/*
-		 * All refer format tags start with '%'.  If we have one
+		 * Refer format tags start with '%'.  If we have one
 		 * that doesn't, assume that it comes from endx2xml
 		 * and just copy and paste to output
 		 */
-		t = fields_tag( endin, i, FIELDS_CHRP );
-		if ( t[0]!='%' ) {
-			fstatus = fields_add( info, t, d->data, endin->level[i] );
+		if ( str_has_value( intag ) && intag->data[0]!='%' ) {
+			fstatus = fields_add( bibout, str_cstr( intag ), str_cstr( invalue ), bibin->level[i] );
 			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 			continue;
 		}
 
-		n = translate_oldtag( t, reftype, all, nall, &process, &level, &newtag );
-		if ( n==-1 ) {
-			endin_notag( p, t, d->data );
+		if ( !translate_oldtag( str_cstr( intag ), reftype, p->all, p->nall, &process, &level, &outtag ) ) {
+			endin_notag( p, str_cstr( intag ), str_cstr( invalue ) );
 			continue;
 		}
-		if ( process == ALWAYS ) continue; /* add these later */
 
-		fields_setused( endin, i );
-
-		switch ( process ) {
-
-		case SIMPLE:
-			status = endin_simple( info, newtag, d->data, level );
-			break;
-
-		case TYPE:
-			status = endin_addtype( info, d->data, level );
-			break;
-
-		case TITLE:
-			ok = title_process( info, newtag, d->data, level, p->nosplittitle );
-			if ( !ok ) status = BIBL_ERR_MEMERR;
-			status = BIBL_OK;
-			break;
-
-		case PERSON:
-			ok = name_add( info, newtag, d->data, level, &(p->asis), &(p->corps) );
-			if ( !ok ) status = BIBL_ERR_MEMERR;
-			status = BIBL_OK;
-			break;
-
-		case DATE:
-			status = endin_adddate( info, t, newtag,d->data,level);
-			break;
-
-		case PAGES:
-			status = endin_addpage( info, d->data, level );
-			break;
-
-		case SERIALNO:
-			ok = addsn( info, d->data, level );
-			if ( !ok ) status = BIBL_ERR_MEMERR;
-			status = BIBL_OK;
-			break;
-
-		case NOTES:
-			status = endin_addnotes( info, newtag, d->data, level );
-			break;
-
-		default:
-			fprintf(stderr,"%s: internal error -- illegal process number %d\n", p->progname, process );
-			status = BIBL_OK;
-			break;
-		}
+		fields_setused( bibin, i );
 
+		status = convertfns[ process ]( bibin, i, intag, invalue, level, p, outtag, bibout );
 		if ( status!=BIBL_OK ) return status;
 
 	}
diff --git a/bibutils/endin.h b/bibutils/endin.h
deleted file mode 100644
--- a/bibutils/endin.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * endin.h
- *
- * Copyright (c) Chris Putnam 2003-2014
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef ENDIN_H
-#define ENDIN_H
-
-#include "newstr.h"
-#include "fields.h"
-#include "reftypes.h"
-#include "bibutils.h"
-
-extern int endin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset );
-extern int endin_processf( fields *endin, char *p, char *filename, long nref );
-extern int endin_typef( fields *endin, char *filename, int nrefs, param *p, variants *all, int nall );
-extern int endin_convertf( fields *endin, fields *info, int reftype, param *p, variants *all, int nall );
-extern int endin_cleanf( bibl *bin, param *p );
-
-extern void endin_initparams( param *p, const char *progname );
-
-extern variants end_all[];
-extern int end_nall;
-
-
-#endif
-
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-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -11,13 +11,18 @@
 #include <string.h>
 #include <ctype.h>
 #include "utf8.h"
-#include "newstr.h"
+#include "str.h"
 #include "strsearch.h"
 #include "fields.h"
-#include "doi.h"
 #include "name.h"
-#include "endout.h"
+#include "title.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 );
+
+
 void
 endout_initparams( param *p, const char *progname )
 {
@@ -142,7 +147,7 @@
 } match_type;
 
 static int
-get_type( fields *info, param *p, unsigned long refnum )
+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. */
@@ -257,20 +262,20 @@
 	char *tag, *data;
 
 	/* Determine type from genre information */
-	for ( i=0; i<info->n; ++i ) {
-		tag = info->tag[i].data;
+	for ( i=0; i<in->n; ++i ) {
+		tag = fields_tag( in, i, FIELDS_CHRP );
 		if ( strcasecmp( tag, "GENRE" )!=0 &&
 		     strcasecmp( tag, "NGENRE" )!=0 ) continue;
-		data = info->data[i].data;
+		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( info, i );
+				fields_setused( in, i );
 			}
 		}
 		if ( p->verbose ) {
 			if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-			fprintf( stderr, "Type from tag '%s' data '%s': ", info->tag[i].data, info->data[i].data );
+			fprintf( stderr, "Type from tag '%s' data '%s': ", tag, data );
 			write_type( stderr, type );
 			fprintf( stderr, "\n" );
 		}
@@ -280,17 +285,17 @@
 			else if ( !strcasecmp( data, "thesis" ) )
 				type = TYPE_THESIS;
 			else if ( !strcasecmp( data, "book" ) ) {
-				if ( info->level[i]==0 ) type = TYPE_BOOK;
+				if ( in->level[i]==0 ) type = TYPE_BOOK;
 				else type = TYPE_INBOOK;
 			}
 			else if ( !strcasecmp( data, "collection" ) ) {
-				if ( info->level[i]==0 ) type = TYPE_BOOK;
+				if ( in->level[i]==0 ) type = TYPE_BOOK;
 				else type = TYPE_INBOOK;
 			}
-			if ( type!=TYPE_UNKNOWN ) fields_setused( info, i );
+			if ( type!=TYPE_UNKNOWN ) fields_setused( in, i );
 		}
 		/* the inbook type should be defined if 'book' in host */
-		if ( type==TYPE_BOOK && info->level[i]>0 ) type = TYPE_INBOOK;
+		if ( type==TYPE_BOOK && in->level[i]>0 ) type = TYPE_INBOOK;
 	}
 	if ( p->verbose ) {
 		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
@@ -301,15 +306,15 @@
 
 	/* Determine from resource information */
 	if ( type==TYPE_UNKNOWN ) {
-		for ( i=0; i<info->n; ++i ) {
-			if ( strcasecmp( info->tag[i].data, "RESOURCE" ) )
+		for ( i=0; i<in->n; ++i ) {
+			if ( strcasecmp( fields_tag( in, i, FIELDS_CHRP ), "RESOURCE" ) )
 				continue;
-			data = info->data[i].data;
+			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( info, i );
+			if ( type!=TYPE_UNKNOWN ) fields_setused( in, i );
 		}
 		if ( p->verbose ) {
 			if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
@@ -321,12 +326,12 @@
 
 	/* Determine from issuance information */
 	if ( type==TYPE_UNKNOWN ) {
-		for ( i=0; i<info->n; ++i ) {
-			if ( strcasecmp( info->tag[i].data, "ISSUANCE" ) )
+		for ( i=0; i<in->n; ++i ) {
+			if ( strcasecmp( fields_tag( in, i, FIELDS_CHRP ), "ISSUANCE" ) )
 				continue;
-			data = info->data[i].data;
+			data = fields_value( in, i, FIELDS_CHRP );
 			if ( !strcasecmp( data, "monographic" ) ) {
-				if ( info->level[i]==0 ) type = TYPE_BOOK;
+				if ( in->level[i]==0 ) type = TYPE_BOOK;
 				else type = TYPE_INBOOK;
 			}
 		}
@@ -340,14 +345,14 @@
 
 	/* default to generic or book chapter, depending on maxlevel */
 	if ( type==TYPE_UNKNOWN ) {
-		maxlevel = fields_maxlevel( info );
+		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( info, "REFNUM", -1 );
+			n = fields_find( in, "REFNUM", -1 );
 			if ( n!=-1 )
-				fprintf( stderr, " %s", info->data[n].data );
+				fprintf( stderr, " %s", (char *) fields_value( in, n, FIELDS_CHRP ) );
 			fprintf( stderr, " (defaulting to generic)\n" );
 			type = TYPE_GENERIC;
 		}
@@ -364,7 +369,7 @@
 }
 
 static void
-output_type( FILE *fp, int type, param *p )
+append_type( int type, fields *out, param *p, int *status )
 {
 	/* These are restricted to Endnote-defined types */
 	match_type genrenames[] = {
@@ -410,315 +415,338 @@
 		{ "Unpublished Work",       TYPE_UNPUBLISHED },
 	};
 	int ngenrenames = sizeof( genrenames ) / sizeof( genrenames[0] );
-	int i, found = 0;
-	fprintf( fp, "%%0 ");
+	int i, fstatus, found = 0;
 	for ( i=0; i<ngenrenames && !found; ++i ) {
 		if ( genrenames[i].type == type ) {
-			fprintf( fp, "%s", genrenames[i].name );
+			fstatus = fields_add( out, "%0", genrenames[i].name, LEVEL_MAIN );
+			if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 			found = 1;
 		}
 	}
 	if ( !found ) {
-		fprintf( fp, "Generic" );
+		fstatus = fields_add( out, "%0", "Generic", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
 		fprintf( stderr, "Cannot identify type %d\n", type );
 	}
-	fprintf( fp, "\n" );
 }
 
-static void
-output_title( FILE *fp, fields *info, char *full, char *sub, char *endtag, 
-		int level )
+static int
+append_title( fields *in, char *full, char *sub, char *endtag,
+		int level, fields *out, int *status )
 {
-	newstr *mainttl = fields_findv( info, level, FIELDS_STRP, full );
-	newstr *subttl  = fields_findv( info, level, FIELDS_STRP, sub );
+	str *mainttl = fields_findv( in, level, FIELDS_STRP, full );
+	str *subttl  = fields_findv( in, level, FIELDS_STRP, sub );
+	str fullttl;
+	int fstatus;
 
-	if ( !mainttl ) return;
+	str_init( &fullttl );
+	title_combine( &fullttl, mainttl, subttl );
 
-	fprintf( fp, "%s %s", endtag, mainttl->data );
-	if ( subttl ) {
-		if ( mainttl->len > 0 &&
-		     mainttl->data[ mainttl->len-1 ]!='?' )
-				fprintf( fp, ":" );
-		fprintf( fp, " %s", subttl->data );
+	if ( str_memerr( &fullttl ) ) {
+		*status = BIBL_ERR_MEMERR;
+		goto out;
 	}
-	fprintf( fp, "\n" );
+
+	if ( str_has_value( &fullttl ) ) {
+		fstatus = fields_add( out, endtag, str_cstr( &fullttl ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+out:
+	str_free( &fullttl );
+	return 1;
 }
 
 static void
-output_people( FILE *fp, fields *info, char *tag, char *entag, int level )
+append_people( fields *in, char *tag, char *entag, int level, fields *out, int *status )
 {
-	newstr oneperson;
-	int i, n, flvl;
+	int i, n, flvl, fstatus;
+	str oneperson;
 	char *ftag;
-	newstr_init( &oneperson );
-	n = fields_num( info );
+
+	str_init( &oneperson );
+	n = fields_num( in );
 	for ( i=0; i<n; ++i ) {
-		flvl = fields_level( info, i );
+		flvl = fields_level( in, i );
 		if ( level!=LEVEL_ANY && flvl!=level ) continue;
-		ftag = fields_tag( info, i, FIELDS_CHRP );
+		ftag = fields_tag( in, i, FIELDS_CHRP );
 		if ( !strcasecmp( ftag, tag ) ) {
-			name_build_withcomma( &oneperson, fields_value( info, i, FIELDS_CHRP ) );
-			fprintf( fp, "%s %s\n", entag, oneperson.data );
+			name_build_withcomma( &oneperson, fields_value( in, i, FIELDS_CHRP ) );
+			fstatus = fields_add_can_dup( out, entag, str_cstr( &oneperson ), LEVEL_MAIN );
+			if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 		}
 	}
-	newstr_free( &oneperson );
+	str_free( &oneperson );
 }
 
 static void
-output_pages( FILE *fp, fields *info )
+append_pages( fields *in, fields *out, int *status )
 {
-	char *sn = fields_findv( info, LEVEL_ANY, FIELDS_CHRP, "PAGESTART" );
-	char *en = fields_findv( info, LEVEL_ANY, FIELDS_CHRP, "PAGEEND" );
+	str *sn, *en;
+	int fstatus;
+	str pages;
 	char *ar;
+
+	sn = fields_findv( in, LEVEL_ANY, FIELDS_STRP, "PAGES:START" );
+	en = fields_findv( in, LEVEL_ANY, FIELDS_STRP, "PAGES:STOP" );
 	if ( sn || en ) {
-		fprintf( fp, "%%P ");
-		if ( sn ) fprintf( fp, "%s", sn );
-		if ( sn && en ) fprintf( fp, "-" );
-		if ( en ) fprintf( fp, "%s", en );
-		fprintf( fp, "\n" );
+		str_init( &pages );
+		if ( sn ) str_strcpy( &pages, sn );
+		if ( sn && en ) str_strcatc( &pages, "-" );
+		if ( en ) str_strcat( &pages, en );
+		if ( str_memerr( &pages ) ) { *status = BIBL_ERR_MEMERR; str_free( &pages ); return; }
+		fstatus = fields_add( out, "%P", str_cstr( &pages ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+		str_free( &pages );
 	} else {
-		ar = fields_findv( info, LEVEL_ANY, FIELDS_CHRP, "ARTICLENUMBER" );
-		if ( ar ) fprintf( fp, "%%P %s\n", ar );
+		ar = fields_findv( in, LEVEL_ANY, FIELDS_CHRP, "ARTICLENUMBER" );
+		if ( ar ) {
+			fstatus = fields_add( out, "%P", ar, LEVEL_MAIN );
+			if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+		}
 	}
 }
 
 static void
-output_doi( FILE *fp, fields *f )
+append_urls( fields *in, fields *out, int *status )
 {
-	newstr doi_url;
-	int i, n;
-
-	newstr_init( &doi_url );
+	int lstatus;
+	slist types;
 
-	n = fields_num( f );
-	for ( i=0; i<n; ++i ) {
-		if ( !fields_match_tag( f, i, "DOI" ) ) continue;
-		doi_to_url( f, i, "URL", &doi_url );
-		if ( doi_url.len )
-			fprintf( fp, "%%U %s\n", doi_url.data );
+	lstatus = slist_init_valuesc( &types, "URL", "DOI", "PMID", "PMC", "ARXIV", "JSTOR", "MRNUMBER", NULL );
+	if ( lstatus!=SLIST_OK ) {
+		*status = BIBL_ERR_MEMERR;
+		return;
 	}
 
-	newstr_free( &doi_url );
-}
-
-static void
-output_pmid( FILE *fp, fields *f )
-{
-	newstr pmid_url;
-	int i, n;
-
-	newstr_init( &pmid_url );
-
-	n = fields_num( f );
-	for ( i=0; i<n; ++i ) {
-		if ( !fields_match_tag( f, i, "PMID" ) ) continue;
-		pmid_to_url( f, i, "URL", &pmid_url );
-		if ( pmid_url.len )
-			fprintf( fp, "%%U %s\n", pmid_url.data );
-	}
+	*status = urls_merge_and_add( in, LEVEL_ANY, out, "%U", LEVEL_MAIN, &types );
 
-	newstr_free( &pmid_url );
+	slist_free( &types );
 }
 
 static void
-output_arxiv( FILE *fp, fields *f )
+append_year( fields *in, fields *out, int *status )
 {
-	newstr arxiv_url;
-	int i, n;
-
-	newstr_init( &arxiv_url );
+	int fstatus;
+	char *year;
 
-	n = fields_num( f );
-	for ( i=0; i<n; ++i ) {
-		if ( !fields_match_tag( f, i, "ARXIV" ) ) continue;
-		arxiv_to_url( f, i, "URL", &arxiv_url );
-		if ( arxiv_url.len )
-			fprintf( fp, "%%U %s\n", arxiv_url.data );
+	year = fields_findv_firstof( in, LEVEL_ANY, FIELDS_CHRP, "DATE:YEAR", "PARTDATE:YEAR", NULL );
+	if ( year ) {
+		fstatus = fields_add( out, "%D", year, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	}
-
-	newstr_free( &arxiv_url );
 }
 
 static void
-output_jstor( FILE *fp, fields *f )
-{
-	newstr jstor_url;
-	int i, n;
-
-	newstr_init( &jstor_url );
-
-	n = fields_num( f );
-	for ( i=0; i<n; ++i ) {
-		if ( !fields_match_tag( f, i, "JSTOR" ) ) continue;
-		jstor_to_url( f, i, "URL", &jstor_url );
-		if ( jstor_url.len )
-			fprintf( fp, "%%U %s\n", jstor_url.data );
-	}
-
-	newstr_free( &jstor_url );
-}
-
-static void
-output_year( FILE *fp, fields *info, int level )
-{
-	char *year = fields_findv_firstof( info, level, FIELDS_CHRP,
-			"YEAR", "PARTYEAR", NULL );
-	if ( year )
-		fprintf( fp, "%%D %s\n", year );
-}
-
-static void
-output_monthday( FILE *fp, fields *info, int level )
+append_monthday( fields *in, fields *out, int *status )
 {
 	char *months[12] = { "January", "February", "March", "April",
 		"May", "June", "July", "August", "September", "October",
 		"November", "December" };
-	int m;
-	char *month = fields_findv_firstof( info, level, FIELDS_CHRP,
-			"MONTH", "PARTMONTH", NULL );
-	char *day   = fields_findv_firstof( info, level, FIELDS_CHRP,
-			"DAY", "PARTDAY", NULL );
+	char *month, *day;
+	int m, fstatus;
+	str monday;
+
+	str_init( &monday );
+	month = fields_findv_firstof( in, LEVEL_ANY, FIELDS_CHRP, "DATE:MONTH", "PARTDATE:MONTH", NULL );
+	day   = fields_findv_firstof( in, LEVEL_ANY, FIELDS_CHRP, "DATE:DAY",   "PARTDATE:DAY",   NULL );
 	if ( month || day ) {
-		fprintf( fp, "%%8 " );
 		if ( month ) {
 			m = atoi( month );
-			if ( m>0 && m<13 ) fprintf( fp, "%s", months[m-1] );
-			else fprintf( fp, "%s", month );
+			if ( m>0 && m<13 ) str_strcpyc( &monday, months[m-1] );
+			else str_strcpyc( &monday, month );
 		}
-		if ( month && day ) fprintf( fp, " " );
-		if ( day ) fprintf( fp, "%s", day );
-		fprintf( fp, "\n" );
+		if ( month && day ) str_strcatc( &monday, " " );
+		if ( day ) str_strcatc( &monday, day );
+		fstatus = fields_add( out, "%8", str_cstr( &monday ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	}
+	str_free( &monday );
 }
 
 static void
-output_thesishint( FILE *fp, int type )
+append_thesishint( int type, fields *out, int *status )
 {
-	if ( type==TYPE_MASTERSTHESIS )
-		fprintf( fp, "%%9 Masters thesis\n" );
-	else if ( type==TYPE_PHDTHESIS )
-		fprintf( fp, "%%9 Ph.D. thesis\n" );
-	else if ( type==TYPE_DIPLOMATHESIS )
-		fprintf( fp, "%%9 Diploma thesis\n" );
-	else if ( type==TYPE_DOCTORALTHESIS )
-		fprintf( fp, "%%9 Doctoral thesis\n" );
-	else if ( type==TYPE_HABILITATIONTHESIS )
-		fprintf( fp, "%%9 Habilitation thesis\n" );
+	int fstatus;
+
+	if ( type==TYPE_MASTERSTHESIS ) {
+		fstatus = fields_add( out, "%9", "Masters thesis", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+	else if ( type==TYPE_PHDTHESIS ) {
+		fstatus = fields_add( out, "%9", "Ph.D. thesis", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+	else if ( type==TYPE_DIPLOMATHESIS ) {
+		fstatus = fields_add( out, "%9", "Diploma thesis", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+	else if ( type==TYPE_DOCTORALTHESIS ) {
+		fstatus = fields_add( out, "%9", "Doctoral thesis", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+	else if ( type==TYPE_HABILITATIONTHESIS ) {
+		fstatus = fields_add( out, "%9", "Habilitation thesis", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 }
 
 static void
-output_easyall( FILE *fp, fields *info, char *tag, char *entag, int level )
+append_easyall( fields *in, char *tag, char *entag, int level, fields *out, int *status )
 {
+	vplist_index i;
+	int fstatus;
 	vplist a;
-	int i;
 	vplist_init( &a );
-	fields_findv_each( info, level, FIELDS_CHRP, &a, tag );
-	for ( i=0; i<a.n; ++i )
-		fprintf( fp, "%s %s\n", entag, (char *) vplist_get( &a, i ) );
+	fields_findv_each( in, level, FIELDS_CHRP, &a, tag );
+	for ( i=0; i<a.n; ++i ) {
+		fstatus = fields_add( out, entag, (char *) vplist_get( &a, i ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 	vplist_free( &a );
 }
 
 static void
-output_easy( FILE *fp, fields *info, char *tag, char *entag, int level )
+append_easy( fields *in, char *tag, char *entag, int level, fields *out, int *status )
 {
-	char *value = fields_findv( info, level, FIELDS_CHRP, tag );
-	if ( value ) fprintf( fp, "%s %s\n", entag, value );
+	char *value;
+	int fstatus;
+
+	value = fields_findv( in, level, FIELDS_CHRP, tag );
+	if ( value ) {
+		fstatus = fields_add( out, entag, value, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 }
 
-void
-endout_write( fields *info, FILE *fp, param *p, unsigned long refnum )
+static int
+append_data( fields *in, fields *out, param *p, unsigned long refnum )
 {
-	int type;
+	int added, type, status = BIBL_OK;
 
-	fields_clearused( info );
+	fields_clearused( in );
 
-	type = get_type( info, p, refnum );
+	type = get_type( in, p, refnum );
 
-	output_type( fp, type, p );
+	append_type( type, out, p, &status );
 
-	output_title( fp, info, "TITLE",      "SUBTITLE",      "%T", LEVEL_MAIN );
-	output_title( fp, info, "SHORTTITLE", "SHORTSUBTITLE", "%!", LEVEL_MAIN );
+	added = append_title( in, "TITLE",      "SUBTITLE",      "%T", LEVEL_MAIN, out, &status );
+	if ( added==0 ) append_title( in, "SHORTTITLE", "SHORTSUBTITLE", "%T", LEVEL_MAIN, out, &status );
+	else            append_title( in, "SHORTTITLE", "SHORTSUBTITLE", "%!", LEVEL_MAIN, out, &status );
 
-	output_people( fp, info, "AUTHOR",     "%A", LEVEL_MAIN );
-	output_people( fp, info, "EDITOR",     "%E", LEVEL_MAIN );
+	append_people( in, "AUTHOR",     "%A", LEVEL_MAIN, out, &status );
+	append_people( in, "EDITOR",     "%E", LEVEL_MAIN, out, &status );
 	if ( type==TYPE_ARTICLE || type==TYPE_MAGARTICLE || type==TYPE_ELECTRONICARTICLE || type==TYPE_NEWSARTICLE )
-		output_people( fp, info, "EDITOR", "%E", LEVEL_HOST );
+		append_people( in, "EDITOR", "%E", LEVEL_HOST, out, &status );
 	else if ( type==TYPE_INBOOK || type==TYPE_INPROCEEDINGS ) {
-		output_people( fp, info, "EDITOR", "%E", LEVEL_HOST );
+		append_people( in, "EDITOR", "%E", LEVEL_HOST, out, &status );
 	} else {
-		output_people( fp, info, "EDITOR", "%Y", LEVEL_HOST );
+		append_people( in, "EDITOR", "%Y", LEVEL_HOST, out, &status );
 	}
-	output_people( fp, info, "TRANSLATOR", "%H", LEVEL_ANY  );
+	append_people( in, "TRANSLATOR", "%H", LEVEL_ANY,    out, &status  );
 
-	output_people( fp, info, "AUTHOR",     "%Y", LEVEL_SERIES );
-	output_people( fp, info, "EDITOR",     "%Y", LEVEL_SERIES );
+	append_people( in, "AUTHOR",     "%Y", LEVEL_SERIES, out, &status );
+	append_people( in, "EDITOR",     "%Y", LEVEL_SERIES, out, &status );
 
 	if ( type==TYPE_CASE )
-		output_easy(    fp, info, "AUTHOR:CORP", "%I", LEVEL_MAIN );
+		append_easy(    in, "AUTHOR:CORP", "%I", LEVEL_MAIN, out, &status );
 	else if ( type==TYPE_HEARING )
-		output_easyall( fp, info, "AUTHOR:CORP", "%S", LEVEL_MAIN );
+		append_easyall( in, "AUTHOR:CORP", "%S", LEVEL_MAIN, out, &status );
 	else if ( type==TYPE_NEWSARTICLE )
-		output_people(  fp, info, "REPORTER",    "%A", LEVEL_MAIN );
+		append_people(  in, "REPORTER",    "%A", LEVEL_MAIN, out, &status );
 	else if ( type==TYPE_COMMUNICATION )
-		output_people(  fp, info, "RECIPIENT",   "%E", LEVEL_ANY  );
+		append_people(  in, "RECIPIENT",   "%E", LEVEL_ANY,  out, &status  );
 	else {
-		output_easyall( fp, info, "AUTHOR:CORP",     "%A", LEVEL_MAIN );
-		output_easyall( fp, info, "AUTHOR:ASIS",     "%A", LEVEL_MAIN );
-		output_easyall( fp, info, "EDITOR:CORP",     "%E", LEVEL_ANY  );
-		output_easyall( fp, info, "EDITOR:ASIS",     "%E", LEVEL_ANY  );
-		output_easyall( fp, info, "TRANSLATOR:CORP", "%H", LEVEL_ANY  );
-		output_easyall( fp, info, "TRANSLATOR:ASIS", "%H", LEVEL_ANY  );
+		append_easyall( in, "AUTHOR:CORP",     "%A", LEVEL_MAIN, out, &status );
+		append_easyall( in, "AUTHOR:ASIS",     "%A", LEVEL_MAIN, out, &status );
+		append_easyall( in, "EDITOR:CORP",     "%E", LEVEL_ANY,  out, &status  );
+		append_easyall( in, "EDITOR:ASIS",     "%E", LEVEL_ANY,  out, &status  );
+		append_easyall( in, "TRANSLATOR:CORP", "%H", LEVEL_ANY,  out, &status  );
+		append_easyall( in, "TRANSLATOR:ASIS", "%H", LEVEL_ANY,  out, &status  );
 	}
 
-	if ( type==TYPE_ARTICLE || type==TYPE_MAGARTICLE || type==TYPE_ELECTRONICARTICLE || type==TYPE_NEWSARTICLE )
-		output_title( fp, info, "TITLE", "SUBTITLE", "%J", LEVEL_HOST );
+	if ( type==TYPE_ARTICLE || type==TYPE_MAGARTICLE || type==TYPE_ELECTRONICARTICLE || type==TYPE_NEWSARTICLE ) {
+		added = append_title( in, "TITLE", "SUBTITLE", "%J", LEVEL_HOST, out, &status );
+		if ( added==0 ) append_title( in, "SHORTTITLE", "SHORTSUBTITLE", "%J", LEVEL_HOST, out, &status );
+	}
+
 	else if ( type==TYPE_INBOOK || type==TYPE_INPROCEEDINGS ) {
-		output_title( fp, info, "TITLE", "SUBTITLE", "%B", LEVEL_HOST );
-	} else {
-		output_title( fp, info, "TITLE", "SUBTITLE", "%S", LEVEL_HOST );
+		added = append_title( in, "TITLE", "SUBTITLE", "%B", LEVEL_HOST, out, &status );
+		if ( added==0 ) append_title( in, "SHORTTITLE", "SHORTSUBTITLE", "%B", LEVEL_HOST, out, &status );
 	}
 
+	else {
+		added = append_title( in, "TITLE", "SUBTITLE", "%S", LEVEL_HOST, out, &status );
+		if ( added==0 ) append_title( in, "SHORTTITLE", "SHORTSUBTITLE", "%S", LEVEL_HOST, out, &status );
+	}
+
 	if ( type!=TYPE_CASE && type!=TYPE_HEARING ) {
-		output_title( fp, info, "TITLE", "SUBTITLE", "%S", LEVEL_SERIES );
+		append_title( in, "TITLE", "SUBTITLE", "%S", LEVEL_SERIES, out, &status );
 	}
 
-	output_year( fp, info, LEVEL_ANY );
-	output_monthday( fp, info, LEVEL_ANY );
+	append_year    ( in, out, &status );
+	append_monthday( in, out, &status );
 
-	output_easy( fp, info, "VOLUME",             "%V", LEVEL_ANY );
-	output_easy( fp, info, "ISSUE",              "%N", LEVEL_ANY );
-	output_easy( fp, info, "NUMBER",             "%N", LEVEL_ANY );
-	output_easy( fp, info, "EDITION",            "%7", LEVEL_ANY );
-	output_easy( fp, info, "PUBLISHER",          "%I", LEVEL_ANY );
-	output_easy( fp, info, "ADDRESS",            "%C", LEVEL_ANY );
-	output_easy( fp, info, "DEGREEGRANTOR",      "%C", LEVEL_ANY );
-	output_easy( fp, info, "DEGREEGRANTOR:CORP", "%C", LEVEL_ANY );
-	output_easy( fp, info, "DEGREEGRANTOR:ASIS", "%C", LEVEL_ANY );
-	output_easy( fp, info, "SERIALNUMBER",       "%@", LEVEL_ANY );
-	output_easy( fp, info, "ISSN",               "%@", LEVEL_ANY );
-	output_easy( fp, info, "ISBN",               "%@", LEVEL_ANY );
-	output_easy( fp, info, "LANGUAGE",           "%G", LEVEL_ANY );
-	output_easy( fp, info, "REFNUM",             "%F", LEVEL_ANY );
-	output_easyall( fp, info, "NOTES",           "%O", LEVEL_ANY );
-	output_easy( fp, info, "ABSTRACT",           "%X", LEVEL_ANY );
-	output_easy( fp, info, "CLASSIFICATION",     "%L", LEVEL_ANY );
-	output_easyall( fp, info, "KEYWORD",         "%K", LEVEL_ANY );
-	output_easyall( fp, info, "NGENRE",          "%9", LEVEL_ANY );
-	output_thesishint( fp, type );
-	output_easyall( fp, info, "DOI",             "%R", LEVEL_ANY );
-	output_easyall( fp, info, "URL",             "%U", LEVEL_ANY ); 
-	output_easyall( fp, info, "FILEATTACH",      "%U", LEVEL_ANY ); 
-	output_doi( fp, info );
-	output_pmid( fp, info );
-	output_arxiv( fp, info );
-	output_jstor( fp, info );
-	output_pages( fp, info );
+	append_easy    ( in, "VOLUME",             "%V", LEVEL_ANY, out, &status );
+	append_easy    ( in, "ISSUE",              "%N", LEVEL_ANY, out, &status );
+	append_easy    ( in, "NUMBER",             "%N", LEVEL_ANY, out, &status );
+	append_easy    ( in, "EDITION",            "%7", LEVEL_ANY, out, &status );
+	append_easy    ( in, "PUBLISHER",          "%I", LEVEL_ANY, out, &status );
+	append_easy    ( in, "ADDRESS",            "%C", LEVEL_ANY, out, &status );
+	append_easy    ( in, "DEGREEGRANTOR",      "%C", LEVEL_ANY, out, &status );
+	append_easy    ( in, "DEGREEGRANTOR:CORP", "%C", LEVEL_ANY, out, &status );
+	append_easy    ( in, "DEGREEGRANTOR:ASIS", "%C", LEVEL_ANY, out, &status );
+	append_easy    ( in, "SERIALNUMBER",       "%@", LEVEL_ANY, out, &status );
+	append_easy    ( in, "ISSN",               "%@", LEVEL_ANY, out, &status );
+	append_easy    ( in, "ISBN",               "%@", LEVEL_ANY, out, &status );
+	append_easy    ( in, "LANGUAGE",           "%G", LEVEL_ANY, out, &status );
+	append_easy    ( in, "REFNUM",             "%F", LEVEL_ANY, out, &status );
+	append_easyall ( in, "NOTES",              "%O", LEVEL_ANY, out, &status );
+	append_easy    ( in, "ABSTRACT",           "%X", LEVEL_ANY, out, &status );
+	append_easy    ( in, "CLASSIFICATION"   ,  "%L", LEVEL_ANY, out, &status );
+	append_easyall ( in, "KEYWORD",            "%K", LEVEL_ANY, out, &status );
+	append_easyall ( in, "NGENRE",             "%9", LEVEL_ANY, out, &status );
+	append_thesishint( type, out, &status );
+	append_easyall ( in, "DOI",                "%R", LEVEL_ANY, out, &status );
+	append_easyall ( in, "URL",                "%U", LEVEL_ANY, out, &status );
+	append_easyall ( in, "FILEATTACH",         "%U", LEVEL_ANY, out, &status );
+	append_urls    ( in, out, &status );
+	append_pages   ( in, out, &status );
+
+	return status;
+}
+
+static void
+output( FILE *fp, fields *out )
+{
+	int i;
+
+	for ( i=0; i<out->n; ++i ) {
+		fprintf( fp, "%s %s\n",
+			(char*) fields_tag( out, i, FIELDS_CHRP ),
+			(char*) fields_value( out, i, FIELDS_CHRP )
+		);
+	}
+
 	fprintf( fp, "\n" );
 	fflush( fp );
 }
 
-void
+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 );
diff --git a/bibutils/endout.h b/bibutils/endout.h
deleted file mode 100644
--- a/bibutils/endout.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * endout.h
- *
- * Copyright (c) Chris Putnam 2005-2013
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef ENDOUT_H
-#define ENDOUT_H
-
-#include <stdio.h>
-#include "bibutils.h"
-
-extern void endout_write( fields *info, FILE *fp, param *p, 
-		unsigned long refnum );
-extern void endout_writeheader( FILE *outptr, param *p );
-
-extern void endout_initparams( param *p, const char *progname );
-
-
-#endif
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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -18,7 +18,7 @@
 	{ "%A", "AUTHOR",    PERSON, LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE, LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE, LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE,  LEVEL_MAIN },
 	{ "%J", "TITLE",     TITLE,  LEVEL_HOST }, /* for refer formats */
 	{ "%E", "2ND_AUTHOR",PERSON, LEVEL_MAIN },
@@ -33,7 +33,7 @@
 	{ "%Y", "AUTHOR",   PERSON, LEVEL_SERIES },
 	{ "%S", "TITLE",     TITLE, LEVEL_SERIES },
 	{ "%7", "EDITION",      SIMPLE, LEVEL_MAIN },
-	{ "%8", "MONTH",    DATE,   LEVEL_MAIN },
+	{ "%8", "DATE:MONTH",    DATE,   LEVEL_MAIN },
 	{ "%9", "GENRE",        SIMPLE, LEVEL_MAIN },
 	{ "%?", "SUB_AUTHOR",   PERSON, LEVEL_MAIN }, /* subsidiary-authors */
 	{ "%!", "SHORTTITLE",   TITLE,  LEVEL_MAIN },
@@ -53,7 +53,7 @@
 	{ "%K", "KEYWORD",      SIMPLE, LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",     SIMPLE, LEVEL_MAIN },
 	{ "%O", "NOTES",        NOTES, LEVEL_MAIN },
-	{ "%U", "URL",          SIMPLE, LEVEL_MAIN },
+	{ "%U", "URL",          URL,    LEVEL_MAIN },
 	{ "%>", "PDFLINK",      SIMPLE, LEVEL_MAIN },
 	{ "%Z", "NOTES",        SIMPLE, LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",  SIMPLE, LEVEL_MAIN },  /* physical location */
@@ -70,7 +70,7 @@
 	{ "%A", "AUTHOR",       PERSON,   LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",   PERSON,   LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE",SIMPLE, LEVEL_MAIN },
-	{ "%D", "PARTYEAR",     SIMPLE,   LEVEL_MAIN },
+	{ "%D", "PARTDATE:YEAR",     SIMPLE,   LEVEL_MAIN },
 	{ "%T", "TITLE" ,       TITLE,    LEVEL_MAIN },
 	{ "%J", "TITLE",        TITLE,    LEVEL_HOST }, /* journal title */
 	{ "%B", "TITLE",        TITLE,    LEVEL_HOST }, /* journal title */
@@ -79,7 +79,7 @@
 	{ "%N", "ISSUE",        SIMPLE,   LEVEL_MAIN },
 	{ "%P", "PAGES",        PAGES,    LEVEL_MAIN },
 	{ "%I", "PUBLISHER",    SIMPLE,   LEVEL_HOST }, 
-	{ "%8", "PARTMONTH",    DATE,     LEVEL_MAIN },
+	{ "%8", "PARTDATE:MONTH",    DATE,     LEVEL_MAIN },
 	{ "%9", "GENRE",        SIMPLE,   LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",   TITLE,    LEVEL_MAIN },
 	{ "%@", "SERIALNUMBER", SERIALNO, LEVEL_HOST },
@@ -91,7 +91,7 @@
 	{ "%F", "REFNUM",       SIMPLE,   LEVEL_MAIN },
 	{ "%K", "KEYWORD",      SIMPLE,   LEVEL_MAIN },
 	{ "%X", "ABSTRACT",     SIMPLE,   LEVEL_MAIN },
-	{ "%U", "URL",          SIMPLE,   LEVEL_MAIN },
+	{ "%U", "URL",          URL,      LEVEL_MAIN },
 	{ "%O", "NOTES",        NOTES,    LEVEL_MAIN },
 	{ "%Z", "NOTES",        NOTES,    LEVEL_MAIN },
 	{ "%1", "CUSTOM1",      NOTES,    LEVEL_MAIN },
@@ -120,7 +120,7 @@
 	{ "%A", "AUTHOR",    PERSON,  LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "PARTYEAR",  SIMPLE,  LEVEL_MAIN },
+	{ "%D", "PARTDATE:YEAR",  SIMPLE,  LEVEL_MAIN },
 	{ "%T", "TITLE",     TITLE,   LEVEL_MAIN },
 	{ "%J", "TITLE",     TITLE,   LEVEL_HOST }, /* magazine name */
 	{ "%B", "TITLE",     TITLE,   LEVEL_HOST }, /* magazine name */
@@ -129,7 +129,7 @@
 	{ "%N", "ISSUE",     SIMPLE,  LEVEL_MAIN },
 	{ "%P", "PAGES",     PAGES,   LEVEL_MAIN },
 	{ "%I", "PUBLISHER", SIMPLE,    LEVEL_HOST }, 
-	{ "%8", "PARTMONTH", DATE,    LEVEL_MAIN },
+	{ "%8", "PARTDATE:MONTH", DATE,    LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",TITLE,   LEVEL_MAIN },
 	{ "%@", "SERIALNUMBER",      SERIALNO,LEVEL_HOST },
 	{ "%(", "TITLE",     SIMPLE,  LEVEL_ORIG }, /* original pub */
@@ -140,7 +140,7 @@
 	{ "%F", "REFNUM",    SIMPLE,  LEVEL_MAIN },
 	{ "%K", "KEYWORD",   SIMPLE,  LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE,  LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE,  LEVEL_MAIN },
+	{ "%U", "URL",       URL,     LEVEL_MAIN },
 	{ "%O", "NOTES",     SIMPLE,  LEVEL_MAIN },
 	{ "%Z", "NOTES",     SIMPLE,  LEVEL_MAIN },
 	{ "%1", "CUSTOM1",      NOTES,    LEVEL_MAIN },
@@ -168,7 +168,7 @@
 	{ "%A", "REPORTER",  PERSON,  LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "PARTYEAR",  SIMPLE,  LEVEL_MAIN },
+	{ "%D", "PARTDATE:YEAR",  SIMPLE,  LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE,   LEVEL_MAIN },
 	{ "%J", "TITLE",     TITLE,   LEVEL_HOST }, /* newspaper name */
 	{ "%B", "TITLE",     TITLE,   LEVEL_HOST }, /* newspaper name */
@@ -177,7 +177,7 @@
 	{ "%N", "ISSUE",     SIMPLE,  LEVEL_MAIN },
 	{ "%P", "PAGES",     PAGES,   LEVEL_MAIN },
 	{ "%7", "EDITION",   SIMPLE,  LEVEL_MAIN },
-	{ "%8", "PARTMONTH", DATE,    LEVEL_MAIN },
+	{ "%8", "PARTDATE:MONTH", DATE,    LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",TITLE,   LEVEL_MAIN },
 	{ "%@", "SERIALNUMBER",      SERIALNO, LEVEL_MAIN },
 	{ "%(", "TITLE",     SIMPLE, LEVEL_ORIG }, /* original pub */
@@ -188,7 +188,7 @@
 	{ "%F", "REFNUM",    SIMPLE,  LEVEL_MAIN },
 	{ "%K", "KEYWORD",   SIMPLE,  LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE,  LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE,  LEVEL_MAIN },
+	{ "%U", "URL",       URL,     LEVEL_MAIN },
 	{ "%O", "NOTES",     SIMPLE,  LEVEL_MAIN },
 	{ "%Z", "NOTES",     SIMPLE,  LEVEL_MAIN },
 	{ "%1", "CUSTOM1",      NOTES,    LEVEL_MAIN },
@@ -215,7 +215,7 @@
 	{ "%A", "AUTHOR",    PERSON,    LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE,    LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE,    LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE,     LEVEL_MAIN },
 	{ "%E", "AUTHOR",    PERSON,    LEVEL_HOST }, /* SERIES_AUTHOR */
 	{ "%B", "TITLE",     TITLE,     LEVEL_HOST }, /* SERIES_TITLE */
@@ -225,7 +225,7 @@
 	{ "%V", "VOLUME" ,   SIMPLE,    LEVEL_MAIN },
 	{ "%N", "NUMBER",    SIMPLE,    LEVEL_MAIN },
 	{ "%6", "NUMVOLUMES",SIMPLE,    LEVEL_MAIN },
-	{ "%P", "TOTALPAGES", SIMPLE,LEVEL_MAIN },
+	{ "%P", "PAGES:TOTAL", SIMPLE,LEVEL_MAIN },
 	{ "%7", "EDITION",   SIMPLE,    LEVEL_MAIN },
 	{ "%?", "TRANSLATOR",PERSON,    LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",TITLE,     LEVEL_MAIN },
@@ -244,7 +244,7 @@
 	{ "%K", "KEYWORD",   SIMPLE,    LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE,    LEVEL_MAIN },
 	{ "%O", "NOTES",     NOTES,     LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE,    LEVEL_MAIN },
+	{ "%U", "URL",       URL,       LEVEL_MAIN },
 	{ "%Z", "NOTES",     SIMPLE,    LEVEL_MAIN },
 	{ "%+", "AUTHORADDRESS",SIMPLE, LEVEL_MAIN },
 	{ "%>", "PDFLINK",      SIMPLE, LEVEL_MAIN },
@@ -264,7 +264,7 @@
 	{ "%A", "AUTHOR",    PERSON,    LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE,    LEVEL_HOST },
+	{ "%D", "DATE:YEAR",      SIMPLE,    LEVEL_HOST },
 	{ "%T", "TITLE" ,    TITLE,     LEVEL_MAIN },
 	{ "%E", "EDITOR",    PERSON,    LEVEL_HOST }, /* editor for book */
 	{ "%B", "TITLE",     TITLE,     LEVEL_HOST }, /* book title */
@@ -294,7 +294,7 @@
 	{ "%K", "KEYWORD",   SIMPLE,    LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE,    LEVEL_MAIN },
 	{ "%O", "NOTES",     NOTES,     LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE,    LEVEL_MAIN },
+	{ "%U", "URL",       URL,       LEVEL_MAIN },
 	{ "%Z", "NOTES",     SIMPLE,    LEVEL_MAIN },
 	{ "%+", "AUTHORADDRESS",SIMPLE, LEVEL_MAIN },
 	{ "%>", "PDFLINK",      SIMPLE, LEVEL_MAIN },
@@ -315,7 +315,7 @@
 	{ "%A", "EDITOR",    PERSON,    LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE,    LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE,    LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE,     LEVEL_MAIN },
 	{ "%E", "EDITOR",    PERSON,    LEVEL_HOST }, /* SERIES_EDITOR */
 	{ "%B", "TITLE",     TITLE,     LEVEL_HOST }, /* SERIES_TITLE */
@@ -324,7 +324,7 @@
 	{ "%N", "NUMBER",    SIMPLE,    LEVEL_MAIN },
 	{ "%V", "VOLUME" ,   SIMPLE,    LEVEL_MAIN },
 	{ "%6", "NUMVOLUMES",SIMPLE,    LEVEL_HOST },
-	{ "%P", "TOTALPAGES", SIMPLE,LEVEL_MAIN },
+	{ "%P", "PAGES:TOTAL", SIMPLE,LEVEL_MAIN },
 	{ "%7", "EDITION",   SIMPLE,    LEVEL_MAIN },
 	{ "%?", "TRANSLATOR",PERSON,    LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",TITLE,     LEVEL_MAIN },
@@ -343,7 +343,7 @@
 	{ "%K", "KEYWORD",   SIMPLE,    LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE,    LEVEL_MAIN },
 	{ "%O", "NOTES",     NOTES,     LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE,    LEVEL_MAIN },
+	{ "%U", "URL",       URL,       LEVEL_MAIN },
 	{ "%Z", "NOTES",     NOTES,     LEVEL_MAIN },
 	{ "%+", "AUTHORADDRESS",SIMPLE, LEVEL_MAIN },
 	{ "%>", "PDFLINK",      SIMPLE, LEVEL_MAIN },
@@ -363,13 +363,13 @@
 	{ "%A", "AUTHOR",    PERSON,  LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE,  LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE,  LEVEL_MAIN },
 	{ "%T", "TITLE",     TITLE,   LEVEL_MAIN },
 	{ "%B", "TITLE",     TITLE,   LEVEL_HOST }, /* COLLECTION_TITLE */
 	{ "%C", "ADDRESS",   SIMPLE,  LEVEL_MAIN },
 	{ "%N", "NUMBER",    SIMPLE,  LEVEL_MAIN },
 	{ "%P", "PAGES",     PAGES,   LEVEL_MAIN },
-	{ "%8", "MONTH", DATE,    LEVEL_MAIN },
+	{ "%8", "DATE:MONTH", DATE,    LEVEL_MAIN },
 	{ "%9", "GENRE",     SIMPLE,  LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",TITLE,   LEVEL_MAIN },
 	{ "%M", "ACCESSNUM", SIMPLE,  LEVEL_MAIN },
@@ -378,7 +378,7 @@
 	{ "%K", "KEYWORD",   SIMPLE,  LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE,  LEVEL_MAIN },
 	{ "%O", "NOTES",     NOTES,   LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE,  LEVEL_MAIN },
+	{ "%U", "URL",       URL,     LEVEL_MAIN },
 	{ "%Z", "NOTES",     NOTES,   LEVEL_MAIN },
 	{ "%1", "CUSTOM1",   NOTES,   LEVEL_MAIN },
 	{ "%2", "CUSTOM2",   NOTES,   LEVEL_MAIN },
@@ -403,13 +403,13 @@
 	{ "%A", "AUTHOR",    PERSON,  LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE,  LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE,  LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE,   LEVEL_MAIN },
 	{ "%E", "RECIPIENT", PERSON,  LEVEL_MAIN },
 	{ "%C", "ADDRESS",   SIMPLE,  LEVEL_MAIN },
 	{ "%V", "VOLUME",    SIMPLE,  LEVEL_MAIN },
 	{ "%I", "PUBLISHER", SIMPLE,  LEVEL_MAIN },
-	{ "%8", "MONTH", DATE,    LEVEL_MAIN },
+	{ "%8", "DATE:MONTH", DATE,    LEVEL_MAIN },
 	{ "%9", "GENRE",     SIMPLE,  LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",TITLE,   LEVEL_MAIN },
 	{ "%M", "ACCESSNUM", SIMPLE,  LEVEL_MAIN },
@@ -418,7 +418,7 @@
 	{ "%K", "KEYWORD",   SIMPLE,  LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE,  LEVEL_MAIN },
 	{ "%O", "NOTES",     NOTES,   LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE,  LEVEL_MAIN },
+	{ "%U", "URL",       URL,     LEVEL_MAIN },
 	{ "%Z", "NOTES",     NOTES,   LEVEL_MAIN },
 	{ "%1", "CUSTOM1",   NOTES,   LEVEL_MAIN },
 	{ "%2", "CUSTOM2",   NOTES,   LEVEL_MAIN },
@@ -442,7 +442,7 @@
 	{ "%A", "AUTHOR",    PERSON,  LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE,  LEVEL_HOST },
+	{ "%D", "DATE:YEAR",      SIMPLE,  LEVEL_HOST },
 	{ "%T", "TITLE" ,    TITLE,   LEVEL_MAIN },
 	{ "%E", "EDITOR",    PERSON,  LEVEL_MAIN },
 	{ "%B", "TITLE",     SIMPLE,  LEVEL_HOST }, 
@@ -455,7 +455,7 @@
 	{ "%Y", "EDITOR",    PERSON,  LEVEL_SERIES },
 	{ "%S", "TITLE",     TITLE,   LEVEL_SERIES },
 	{ "%7", "EDITION",   SIMPLE,  LEVEL_HOST },
-	{ "%8", "MONTH", DATE,    LEVEL_MAIN },
+	{ "%8", "DATE:MONTH", DATE,    LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",TITLE,   LEVEL_MAIN },
 	{ "%1", "CUSTOM1",   NOTES,   LEVEL_MAIN },
 	{ "%2", "CUSTOM2",   NOTES,   LEVEL_MAIN },
@@ -469,7 +469,7 @@
 	{ "%K", "KEYWORD",   SIMPLE,  LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE,  LEVEL_MAIN },
 	{ "%O", "NOTES",     NOTES,   LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE,  LEVEL_MAIN },
+	{ "%U", "URL",       URL,     LEVEL_MAIN },
 	{ "%Z", "NOTES",     NOTES,   LEVEL_MAIN },
 	{ "%+", "AUTHORADDRESS",SIMPLE,LEVEL_MAIN},
 	{ "%>", "PDFLINK",      SIMPLE, LEVEL_MAIN },
@@ -488,7 +488,7 @@
 	{ "%A", "AUTHOR",       PERSON,   LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",   PERSON,   LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE",SIMPLE, LEVEL_MAIN },
-	{ "%D", "PARTYEAR",     SIMPLE,   LEVEL_MAIN },
+	{ "%D", "PARTDATE:YEAR",     SIMPLE,   LEVEL_MAIN },
 	{ "%T", "TITLE" ,       TITLE,    LEVEL_MAIN },
 	{ "%J", "TITLE",        TITLE,    LEVEL_HOST }, /* journal title */
 	{ "%B", "TITLE",        TITLE,    LEVEL_HOST }, /* journal title */
@@ -497,7 +497,7 @@
 	{ "%N", "ISSUE",        SIMPLE,   LEVEL_MAIN },
 	{ "%P", "PAGES",        PAGES,    LEVEL_MAIN },
 	{ "%I", "PUBLISHER",    SIMPLE,   LEVEL_HOST }, 
-	{ "%8", "PARTMONTH",    DATE,     LEVEL_MAIN },
+	{ "%8", "PARTDATE:MONTH",    DATE,     LEVEL_MAIN },
 	{ "%9", "GENRE",        SIMPLE,   LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",   TITLE,    LEVEL_MAIN },
 	{ "%@", "SERIALNUMBER", SERIALNO, LEVEL_HOST },
@@ -509,7 +509,7 @@
 	{ "%F", "REFNUM",       SIMPLE,   LEVEL_MAIN },
 	{ "%K", "KEYWORD",      SIMPLE,   LEVEL_MAIN },
 	{ "%X", "ABSTRACT",     SIMPLE,   LEVEL_MAIN },
-	{ "%U", "URL",          SIMPLE,   LEVEL_MAIN },
+	{ "%U", "URL",          URL,      LEVEL_MAIN },
 	{ "%O", "NOTES",        NOTES,    LEVEL_MAIN },
 	{ "%Z", "NOTES",        NOTES,    LEVEL_MAIN },
 	{ "%1", "CUSTOM1",      NOTES,    LEVEL_MAIN },
@@ -535,15 +535,15 @@
 	{ "%A", "AUTHOR",       PERSON, LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",   PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",         SIMPLE, LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",         SIMPLE, LEVEL_MAIN },
 	{ "%T", "TITLE" ,       TITLE,  LEVEL_MAIN },
 	{ "%B", "ACADEMIC_DEPARTMENT", SIMPLE, LEVEL_MAIN },
 	{ "%C", "ADDRESS",      SIMPLE, LEVEL_MAIN },
 	{ "%I", "PUBLISHER",    SIMPLE, LEVEL_MAIN }, 
 	{ "%V", "VOLUME",       SIMPLE, LEVEL_MAIN },
-	{ "%P", "NUMPAGES",     PAGES,  LEVEL_MAIN },
+	{ "%P", "PAGES:TOTAL",  PAGES,  LEVEL_MAIN },
 	{ "%9", "GENRE",        SIMPLE, LEVEL_MAIN }, /* thesis type */
-	{ "%8", "MONTH",        DATE,   LEVEL_MAIN },
+	{ "%8", "DATE:MONTH",   DATE,   LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",   TITLE,  LEVEL_MAIN },
 	{ "%1", "CUSTOM1",      NOTES,  LEVEL_MAIN },
 	{ "%2", "CUSTOM2",      NOTES,  LEVEL_MAIN },
@@ -557,7 +557,7 @@
 	{ "%K", "KEYWORD",      SIMPLE, LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",     SIMPLE, LEVEL_MAIN },
 	{ "%O", "NOTES",        NOTES,  LEVEL_MAIN },
-	{ "%U", "URL",          SIMPLE, LEVEL_MAIN },
+	{ "%U", "URL",          URL,    LEVEL_MAIN },
 	{ "%Z", "NOTES",        NOTES,  LEVEL_MAIN },
 	{ "%+", "AUTHORADDRESS",SIMPLE, LEVEL_MAIN },
 	{ "%>", "PDFLINK",      SIMPLE, LEVEL_MAIN },
@@ -576,7 +576,7 @@
 	{ "%A", "AUTHOR",    PERSON   , LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE   , LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE   , LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE    , LEVEL_MAIN },
 	{ "%C", "ADDRESS",   SIMPLE   , LEVEL_MAIN },
 	{ "%I", "PUBLISHER", SIMPLE   , LEVEL_MAIN },
@@ -588,7 +588,7 @@
 	{ "%K", "KEYWORD",   SIMPLE   , LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE   , LEVEL_MAIN },
 	{ "%O", "NOTES",     SIMPLE   , LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE   , LEVEL_MAIN },
+	{ "%U", "URL",       URL,       LEVEL_MAIN },
 	{ "%Z", "NOTES",     SIMPLE   , LEVEL_MAIN },
 	{ "%1", "COMPUTER",  SIMPLE   , LEVEL_MAIN },
 	{ "%2", "CUSTOM2",   SIMPLE   , LEVEL_MAIN },
@@ -613,14 +613,14 @@
 	{ "%A", "WRITER",    PERSON   , LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE   , LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE   , LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE    , LEVEL_MAIN },
 	{ "%B", "TITLE",     TITLE    , LEVEL_HOST }, /* COLLECTION_TITLE */
 	{ "%C", "ADDRESS",   SIMPLE   , LEVEL_MAIN },
 	{ "%I", "PUBLISHER", SIMPLE   , LEVEL_MAIN },
 	{ "%V", "EXTENTOFWORK",     SIMPLE   , LEVEL_MAIN },
 	{ "%N", "NUMBER",    SIMPLE   , LEVEL_MAIN },
-	{ "%8", "MONTH", DATE     , LEVEL_MAIN },
+	{ "%8", "DATE:MONTH", DATE     , LEVEL_MAIN },
 	{ "%9", "GENRE",     SIMPLE   , LEVEL_MAIN },
 	{ "%?", "PERFORMER", PERSON   , LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",TITLE    , LEVEL_MAIN },
@@ -630,7 +630,7 @@
 	{ "%K", "KEYWORD",   SIMPLE   , LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE   , LEVEL_MAIN },
 	{ "%O", "NOTES",     NOTES,     LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE   , LEVEL_MAIN },
+	{ "%U", "URL",       URL,       LEVEL_MAIN },
 	{ "%Z", "NOTES",     NOTES,     LEVEL_MAIN },
 	{ "%1", "CUSTOM1",   NOTES,     LEVEL_MAIN },
 	{ "%2", "CUSTOM2",   NOTES,     LEVEL_MAIN },
@@ -654,14 +654,14 @@
 	{ "%A", "AUTHOR",       PERSON,   LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",   PERSON,   LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",         SIMPLE,   LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",         SIMPLE,   LEVEL_MAIN },
 	{ "%T", "TITLE" ,       TITLE,    LEVEL_MAIN },
 	{ "%E", "DIRECTOR",     PERSON,   LEVEL_MAIN },
 	{ "%B", "TITLE",        TITLE,    LEVEL_HOST }, /* SERIES_TITLE */
 	{ "%C", "ADDRESS",      SIMPLE,   LEVEL_MAIN },
 	{ "%I", "PUBLISHER",    SIMPLE,   LEVEL_MAIN },
 	{ "%Y", "PRODUCER",     PERSON,   LEVEL_MAIN },
-	{ "%8", "MONTH",        DATE,     LEVEL_MAIN },
+	{ "%8", "DATE:MONTH",        DATE,     LEVEL_MAIN },
 	{ "%9", "GENRE",        SIMPLE,   LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",   TITLE,    LEVEL_MAIN },
 	{ "%@", "SERIALNUMBER", SERIALNO, LEVEL_MAIN },
@@ -672,7 +672,7 @@
 	{ "%K", "KEYWORD",      SIMPLE,   LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",     SIMPLE,   LEVEL_MAIN },
 	{ "%O", "NOTES",        NOTES,    LEVEL_MAIN },
-	{ "%U", "URL",          SIMPLE,   LEVEL_MAIN },
+	{ "%U", "URL",          URL,      LEVEL_MAIN },
 	{ "%Z", "NOTES",        SIMPLE,   LEVEL_MAIN },
 	{ "%1", "CUSTOM1",      NOTES,    LEVEL_MAIN },
 	{ "%2", "CUSTOM2",      NOTES,    LEVEL_MAIN },
@@ -696,7 +696,7 @@
 	{ "%A", "AUTHOR",    PERSON    , LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE    , LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE    , LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE     , LEVEL_MAIN },
 	{ "%C", "ADDRESS",   SIMPLE, LEVEL_MAIN },
 	{ "%I", "PUBLISHER", SIMPLE    , LEVEL_MAIN }, 
@@ -717,7 +717,7 @@
 	{ "%K", "KEYWORD",   SIMPLE,     LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE,     LEVEL_MAIN },
 	{ "%O", "NOTES",     NOTES,      LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE,     LEVEL_MAIN },
+	{ "%U", "URL",       URL,        LEVEL_MAIN },
 	{ "%V", "VOLUME",    SIMPLE,     LEVEL_MAIN },
 	{ "%Z", "NOTES",     NOTES,      LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",  SIMPLE, LEVEL_MAIN },  /* physical location */
@@ -737,7 +737,7 @@
 	{ "%A", "AUTHOR",    PERSON    , LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE    , LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE    , LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE     , LEVEL_MAIN },
 	{ "%C", "ADDRESS",   SIMPLE, LEVEL_MAIN },
 	{ "%I", "PUBLISHER", SIMPLE    , LEVEL_MAIN }, 
@@ -758,7 +758,7 @@
 	{ "%K", "KEYWORD",   SIMPLE,     LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE,     LEVEL_MAIN },
 	{ "%O", "NOTES",     NOTES,      LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE,     LEVEL_MAIN },
+	{ "%U", "URL",       URL,        LEVEL_MAIN },
 	{ "%V", "VOLUME",    SIMPLE,     LEVEL_MAIN },
 	{ "%Z", "NOTES",     NOTES,      LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",  SIMPLE, LEVEL_MAIN },  /* physical location */
@@ -778,11 +778,11 @@
 	{ "%A", "ARTIST",    PERSON   , LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",         SIMPLE   , LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",         SIMPLE   , LEVEL_MAIN },
 	{ "%T", "TITLE" ,       TITLE    , LEVEL_MAIN },
 	{ "%C", "ADDRESS",      SIMPLE   , LEVEL_MAIN },
 	{ "%I", "PUBLISHER",    SIMPLE   , LEVEL_MAIN },
-	{ "%8", "MONTH",        DATE,   LEVEL_MAIN },
+	{ "%8", "DATE:MONTH",        DATE,   LEVEL_MAIN },
 	{ "%9", "GENRE",        SIMPLE, LEVEL_MAIN },
 	{ "%!", "SHORTTITLE",   TITLE,  LEVEL_MAIN },
 	{ "%M", "ACCESSNUM",    SIMPLE, LEVEL_MAIN },
@@ -791,7 +791,7 @@
 	{ "%K", "KEYWORD",      SIMPLE, LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",     SIMPLE, LEVEL_MAIN },
 	{ "%O", "NOTES",        NOTES,  LEVEL_MAIN },
-	{ "%U", "URL",          SIMPLE, LEVEL_MAIN },
+	{ "%U", "URL",          URL,    LEVEL_MAIN },
 	{ "%Z", "NOTES",        NOTES,  LEVEL_MAIN },
 	{ "%1", "CUSTOM1",      NOTES,  LEVEL_MAIN },
 	{ "%2", "CUSTOM2",      NOTES,  LEVEL_MAIN },
@@ -815,12 +815,12 @@
 	{ "%A", "AUTHOR",    PERSON   , LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE   , LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE   , LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE    , LEVEL_MAIN },
 	{ "%C", "ADDRESS",   SIMPLE   , LEVEL_MAIN },
 	{ "%I", "PUBLISHER", SIMPLE, LEVEL_MAIN },
 	{ "%P", "PAGES",     PAGES, LEVEL_MAIN },
-	{ "%8", "MONTH",     DATE, LEVEL_MAIN },
+	{ "%8", "DATE:MONTH",     DATE, LEVEL_MAIN },
 	{ "%9", "GENRE",     SIMPLE,    LEVEL_MAIN },
 	{ "%N", "NUMBER",    SIMPLE,    LEVEL_MAIN },
 	{ "%@", "SERIALNUMBER", SERIALNO, LEVEL_MAIN },
@@ -831,7 +831,7 @@
 	{ "%K", "KEYWORD",   SIMPLE,    LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE,    LEVEL_MAIN },
 	{ "%O", "NOTES",     NOTES,     LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE,    LEVEL_MAIN },
+	{ "%U", "URL",       URL,       LEVEL_MAIN },
 	{ "%Z", "NOTES",     NOTES,     LEVEL_MAIN },
 	{ "%1", "CUSTOM1",   NOTES,     LEVEL_MAIN },
 	{ "%2", "CUSTOM2",   NOTES,     LEVEL_MAIN },
@@ -856,7 +856,7 @@
 	{ "%A", "CARTOGRAPHER",    PERSON   , LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE   , LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE   , LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE    , LEVEL_MAIN },
 	{ "%C", "ADDRESS",   SIMPLE   , LEVEL_MAIN },
 	{ "%I", "PUBLISHER", SIMPLE   , LEVEL_MAIN },
@@ -869,7 +869,7 @@
 	{ "%K", "KEYWORD",   SIMPLE   , LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",  SIMPLE   , LEVEL_MAIN },
 	{ "%O", "NOTES",     SIMPLE   , LEVEL_MAIN },
-	{ "%U", "URL",       SIMPLE   , LEVEL_MAIN },
+	{ "%U", "URL",       URL,       LEVEL_MAIN },
 	{ "%Z", "NOTES",     SIMPLE   , LEVEL_MAIN },
 	{ "%1", "SCALE",     SIMPLE   , LEVEL_MAIN },
 	{ "%2", "CUSTOM2",   SIMPLE   , LEVEL_MAIN },
@@ -894,7 +894,7 @@
 	{ "%A", "AUTHOR",    PERSON, LEVEL_MAIN  },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE, LEVEL_MAIN  },
+	{ "%D", "DATE:YEAR",      SIMPLE, LEVEL_MAIN  },
 	{ "%T", "TITLE" ,    TITLE, LEVEL_MAIN   },
 	{ "%B", "PUBLISHED_SOURCE", SIMPLE, LEVEL_MAIN},
 	{ "%C", "COUNTRY",   SIMPLE, LEVEL_MAIN  },
@@ -909,7 +909,7 @@
 	{ "%K", "KEYWORD",   SIMPLE, LEVEL_MAIN  }, 
 	{ "%X", "ABSTRACT",  SIMPLE, LEVEL_MAIN  },
 	{ "%O", "NOTES",     SIMPLE, LEVEL_MAIN  },
-	{ "%U", "URL",       SIMPLE, LEVEL_MAIN  },
+	{ "%U", "URL",       URL,    LEVEL_MAIN  },
 	{ "%Z", "NOTES",     SIMPLE, LEVEL_MAIN  },
 	{ "%1", "SCALE",     SIMPLE, LEVEL_MAIN  },
 	{ "%2", "CUSTOM2",   SIMPLE, LEVEL_MAIN  },
@@ -931,7 +931,7 @@
 
 static lookups hearing[] = {
 	{ "%0", "INTERNAL_TYPE",         TYPE, LEVEL_MAIN    },
-	{ "%D", "YEAR",         SIMPLE, LEVEL_MAIN  },
+	{ "%D", "DATE:YEAR",         SIMPLE, LEVEL_MAIN  },
 	{ "%H", "TRANSLATOR",   PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
 	{ "%T", "TITLE" ,       TITLE, LEVEL_MAIN   },
@@ -940,10 +940,10 @@
 	{ "%I", "PUBLISHER",    SIMPLE, LEVEL_MAIN  }, 
 	{ "%6", "NUMVOLUMES",   SIMPLE, LEVEL_MAIN  },
 	{ "%N", "NUMBER",       SIMPLE, LEVEL_MAIN  },
-	{ "%P", "NUMPAGES",     PAGES,  LEVEL_MAIN  },
+	{ "%P", "PAGES:TOTAL",  PAGES,  LEVEL_MAIN  },
 	{ "%S", "LEGISLATIVEBODY:CORP", SIMPLE, LEVEL_MAIN  },
 	{ "%7", "SESSION",       SIMPLE, LEVEL_MAIN  },
-	{ "%8", "MONTH",         DATE,   LEVEL_MAIN  },
+	{ "%8", "DATE:MONTH",         DATE,   LEVEL_MAIN  },
 	{ "%!", "SHORTTITLE",    TITLE,  LEVEL_MAIN  },
 	{ "%(", "TITLE",         SIMPLE, LEVEL_ORIG }, /* orig publication */
 	{ "%M", "ACCESSNUM",     SIMPLE, LEVEL_MAIN  },
@@ -952,7 +952,7 @@
 	{ "%K", "KEYWORD",       SIMPLE, LEVEL_MAIN  }, 
 	{ "%X", "ABSTRACT",      SIMPLE, LEVEL_MAIN  },
 	{ "%O", "NOTES",         NOTES,  LEVEL_MAIN  },
-	{ "%U", "URL",           SIMPLE, LEVEL_MAIN  },
+	{ "%U", "URL",           URL,    LEVEL_MAIN  },
 	{ "%Z", "NOTES",         NOTES,  LEVEL_MAIN  },
 	{ "%1", "CUSTOM1",       NOTES,  LEVEL_MAIN  },
 	{ "%2", "CUSTOM2",       NOTES,  LEVEL_MAIN  },
@@ -973,7 +973,7 @@
 
 static lookups bill[] = {
 	{ "%0", "INTERNAL_TYPE",      TYPE, LEVEL_MAIN    },
-	{ "%D", "YEAR",      SIMPLE, LEVEL_MAIN  },
+	{ "%D", "DATE:YEAR",      SIMPLE, LEVEL_MAIN  },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE, LEVEL_MAIN   },
@@ -984,7 +984,7 @@
 	{ "%&", "CODESECTION",SIMPLE, LEVEL_MAIN  },
 	{ "%S", "LEGISLATIVEBODY:CORP", SIMPLE, LEVEL_MAIN  },
 	{ "%7", "SESSION",   SIMPLE, LEVEL_MAIN  },
-	{ "%8", "MONTH", DATE, LEVEL_MAIN    },
+	{ "%8", "DATE:MONTH", DATE, LEVEL_MAIN    },
 	{ "%?", "SPONSOR",   PERSON, LEVEL_MAIN  },
 	{ "%!", "SHORTTITLE",TITLE,  LEVEL_MAIN   },
 	{ "%(", "TITLE",     SIMPLE, LEVEL_ORIG  }, /* original pub */
@@ -994,7 +994,7 @@
 	{ "%K", "KEYWORD",   SIMPLE, LEVEL_MAIN  }, 
 	{ "%X", "ABSTRACT",  SIMPLE, LEVEL_MAIN  },
 	{ "%O", "NOTES",     NOTES,  LEVEL_MAIN  },
-	{ "%U", "URL",       SIMPLE, LEVEL_MAIN  },
+	{ "%U", "URL",       URL,    LEVEL_MAIN  },
 	{ "%Z", "NOTES",     NOTES,  LEVEL_MAIN  },
 	{ "%1", "CUSTOM1",   NOTES,  LEVEL_MAIN  },
 	{ "%2", "CUSTOM2",   NOTES,  LEVEL_MAIN  },
@@ -1015,7 +1015,7 @@
 
 static lookups statute[] = {
 	{ "%0", "INTERNAL_TYPE",      TYPE, LEVEL_MAIN    },
-	{ "%D", "YEAR",      SIMPLE, LEVEL_MAIN  },
+	{ "%D", "DATE:YEAR",      SIMPLE, LEVEL_MAIN  },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE, LEVEL_MAIN   },
@@ -1025,7 +1025,7 @@
 	{ "%P", "PAGES",     PAGES, LEVEL_MAIN    },
 	{ "%&", "SECTION",   SIMPLE, LEVEL_MAIN  },
 	{ "%7", "SESSION",   SIMPLE, LEVEL_MAIN  },
-	{ "%8", "MONTH", DATE, LEVEL_MAIN    },
+	{ "%8", "DATE:MONTH", DATE, LEVEL_MAIN    },
 	{ "%!", "SHORTTITLE",TITLE,  LEVEL_MAIN   },
 	{ "%(", "TITLE",     SIMPLE, LEVEL_ORIG  }, /* original pub */
 	{ "%M", "ACCESSNUM", SIMPLE, LEVEL_MAIN  },
@@ -1034,7 +1034,7 @@
 	{ "%K", "KEYWORD",   SIMPLE, LEVEL_MAIN  }, 
 	{ "%X", "ABSTRACT",  SIMPLE, LEVEL_MAIN  },
 	{ "%O", "NOTES",     NOTES,  LEVEL_MAIN  },
-	{ "%U", "URL",       SIMPLE, LEVEL_MAIN  },
+	{ "%U", "URL",       URL,    LEVEL_MAIN  },
 	{ "%Z", "NOTES",     NOTES,  LEVEL_MAIN  },
 	{ "%1", "CUSTOM1",   NOTES,  LEVEL_MAIN  },
 	{ "%2", "CUSTOM2",   NOTES,  LEVEL_MAIN  },
@@ -1055,7 +1055,7 @@
 
 static lookups lawcase[] = {
 	{ "%0", "INTERNAL_TYPE",      TYPE, LEVEL_MAIN    },
-	{ "%D", "YEAR",      SIMPLE, LEVEL_MAIN  },
+	{ "%D", "DATE:YEAR",      SIMPLE, LEVEL_MAIN  },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE, LEVEL_MAIN   },
@@ -1065,7 +1065,7 @@
 	{ "%V", "VOLUME",    SIMPLE, LEVEL_MAIN   },
 	{ "%N", "PUBLICLAWNUMBER",    SIMPLE, LEVEL_MAIN  },
 	{ "%P", "STARTPAGE",     SIMPLE, LEVEL_MAIN  },
-	{ "%8", "MONTH", DATE, LEVEL_MAIN    },
+	{ "%8", "DATE:MONTH", DATE, LEVEL_MAIN    },
 	{ "%?", "COUNSEL",   PERSON, LEVEL_MAIN  },
 	{ "%!", "SHORTTITLE",TITLE,  LEVEL_MAIN   },
 	{ "%(", "TITLE",     SIMPLE, LEVEL_ORIG  }, /* original pub */
@@ -1075,7 +1075,7 @@
 	{ "%K", "KEYWORD",   SIMPLE, LEVEL_MAIN  }, 
 	{ "%X", "ABSTRACT",  SIMPLE, LEVEL_MAIN  },
 	{ "%O", "NOTES",     NOTES,  LEVEL_MAIN  },
-	{ "%U", "URL",       SIMPLE, LEVEL_MAIN  },
+	{ "%U", "URL",       URL,    LEVEL_MAIN  },
 	{ "%Z", "NOTES",     NOTES,  LEVEL_MAIN  },
 	{ "%1", "CUSTOM1",   NOTES,  LEVEL_MAIN  },
 	{ "%2", "CUSTOM2",   NOTES,  LEVEL_MAIN  },
@@ -1099,7 +1099,7 @@
 	{ "%A", "AUTHOR",    PERSON, LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE, LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE, LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE,  LEVEL_MAIN },
 	{ "%J", "TITLE",     TITLE,  LEVEL_HOST }, /* for refer formats */
 	{ "%E", "2ND_AUTHOR",PERSON, LEVEL_MAIN },
@@ -1114,7 +1114,7 @@
 	{ "%Y", "AUTHOR",    PERSON, LEVEL_SERIES },
 	{ "%S", "TITLE",     TITLE,  LEVEL_SERIES },
 	{ "%7", "EDITION",   SIMPLE, LEVEL_MAIN },
-	{ "%8", "MONTH",     DATE,   LEVEL_MAIN },
+	{ "%8", "DATE:MONTH",     DATE,   LEVEL_MAIN },
 	{ "%9", "GENRE",     SIMPLE, LEVEL_MAIN },
 	{ "%?", "SUB_AUTHOR",   PERSON, LEVEL_MAIN }, /* subsidiary-authors */
 	{ "%!", "SHORTTITLE",   TITLE,  LEVEL_MAIN },
@@ -1134,7 +1134,7 @@
 	{ "%K", "KEYWORD",      SIMPLE, LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",     SIMPLE, LEVEL_MAIN },
 	{ "%O", "NOTES",        NOTES,  LEVEL_MAIN },
-	{ "%U", "URL",          SIMPLE, LEVEL_MAIN },
+	{ "%U", "URL",          URL,    LEVEL_MAIN },
 	{ "%Z", "NOTES",        NOTES,  LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",  SIMPLE, LEVEL_MAIN },  /* physical location */
 	{ "%+", "AUTHORADDRESS",SIMPLE, LEVEL_MAIN },
@@ -1152,7 +1152,7 @@
 	{ "%A", "AUTHOR",    PERSON, LEVEL_MAIN },
 	{ "%H", "TRANSLATOR",PERSON, LEVEL_MAIN },
 	{ "%Q", "TRANSLATEDTITLE", SIMPLE, LEVEL_MAIN },
-	{ "%D", "YEAR",      SIMPLE, LEVEL_MAIN },
+	{ "%D", "DATE:YEAR",      SIMPLE, LEVEL_MAIN },
 	{ "%T", "TITLE" ,    TITLE,  LEVEL_MAIN },
 	{ "%J", "TITLE",     TITLE,  LEVEL_HOST }, /* for refer formats */
 	{ "%E", "2ND_AUTHOR",PERSON, LEVEL_MAIN },
@@ -1167,7 +1167,7 @@
 	{ "%Y", "AUTHOR",   PERSON, LEVEL_SERIES },
 	{ "%S", "TITLE",    TITLE, LEVEL_SERIES },
 	{ "%7", "EDITION",      SIMPLE, LEVEL_MAIN },
-	{ "%8", "MONTH",    DATE,   LEVEL_MAIN },
+	{ "%8", "DATE:MONTH",    DATE,   LEVEL_MAIN },
 	{ "%9", "GENRE",        SIMPLE, LEVEL_MAIN },
 	{ "%?", "SUB_AUTHOR",   PERSON, LEVEL_MAIN }, /* subsidiary-authors */
 	{ "%!", "SHORTTITLE",   TITLE,  LEVEL_MAIN },
@@ -1187,7 +1187,7 @@
 	{ "%K", "KEYWORD",      SIMPLE, LEVEL_MAIN }, 
 	{ "%X", "ABSTRACT",     SIMPLE, LEVEL_MAIN },
 	{ "%O", "NOTES",        NOTES,  LEVEL_MAIN },
-	{ "%U", "URL",          SIMPLE, LEVEL_MAIN },
+	{ "%U", "URL",          URL,    LEVEL_MAIN },
 	{ "%>", "PDFLINK",      SIMPLE, LEVEL_MAIN },
 	{ "%Z", "NOTES",        NOTES,  LEVEL_MAIN },
 	{ "%W", "PHYSICALLOC",  SIMPLE, LEVEL_MAIN },  /* physical location */
diff --git a/bibutils/endxmlin.c b/bibutils/endxmlin.c
--- a/bibutils/endxmlin.c
+++ b/bibutils/endxmlin.c
@@ -1,28 +1,37 @@
 /*
  * endxmlin.c
  *
- * Copyright (c) Chris Putnam 2006-2014
+ * Copyright (c) Chris Putnam 2006-2017
  *
  * Program and source code released under the GPL version 2
  *
  */
 #include <stdio.h>
 #include <stdlib.h>
-#include "newstr.h"
-#include "newstr_conv.h"
+#include "str.h"
+#include "str_conv.h"
 #include "fields.h"
 #include "name.h"
 #include "xml.h"
 #include "xml_encoding.h"
 #include "reftypes.h"
-#include "endxmlin.h"
-#include "endin.h"
+#include "bibformats.h"
 
 typedef struct {
 	char *attrib;
 	char *internal;
 } attribs;
 
+extern variants end_all[];
+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 );
+extern int endin_convertf( fields *endin, fields *info, int reftype, param *p );
+extern int endin_cleanf( bibl *bin, param *p );
+
+
 /*****************************************************
  PUBLIC: void endxmlin_initparams()
 *****************************************************/
@@ -48,8 +57,8 @@
 	p->all      = end_all;
 	p->nall     = end_nall;
 
-	list_init( &(p->asis) );
-	list_init( &(p->corps) );
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
 
 	if ( !progname ) p->progname = NULL;
 	else p->progname = strdup( progname );
@@ -66,14 +75,13 @@
 	return 1;
 }
 
-int
-endxmlin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line,
-	newstr *reference, int *fcharset )
+static int
+endxmlin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
-	newstr tmp;
+	str tmp;
 	char *startptr = NULL, *endptr = NULL;
 	int haveref = 0, inref = 0, done = 0, file_charset = CHARSET_UNKNOWN, m;
-	newstr_init( &tmp );
+	str_init( &tmp );
 	while ( !haveref && !done ) {
 		if ( line->data ) {
 			if ( !inref ) {
@@ -90,21 +98,21 @@
 				int n = 8;
 				char *p = &(line->data[line->len-1]);
 				while ( *p && n ) { p--; n--; }
-				newstr_segdel( line, line->data, p ); 
+				str_segdel( line, line->data, p );
 			}
 		}
 
 		if ( !startptr || !endptr ) {
 			done = xml_readmore( fp, buf, bufsize, bufpos );
-			newstr_strcat( line, buf );
+			str_strcatc( line, buf );
 		} else {
-			/* we can reallocate in the newstr_strcat, so re-find */
+			/* we can reallocate in the str_strcat, so re-find */
 			startptr = xml_findstart( line->data, "RECORD" );
 			endptr = xml_findend( line->data, "RECORD" );
-			newstr_segcpy( reference, startptr, endptr );
+			str_segcpy( reference, startptr, endptr );
 			/* clear out information in line */
-			newstr_strcpy( &tmp, endptr );
-			newstr_newstrcpy( line, &tmp );
+			str_strcpyc( &tmp, endptr );
+			str_strcpy( line, &tmp );
 			haveref = 1;
 		}
 		if ( line->data ) {
@@ -112,7 +120,7 @@
 			if ( m!=CHARSET_UNKNOWN ) file_charset = m;
 		}
 	}
-	newstr_free( &tmp );
+	str_free( &tmp );
 	*fcharset = file_charset;
 	return haveref;
 }
@@ -139,12 +147,13 @@
  *   </datatype>
  */
 static int
-endxmlin_datar( xml *node, newstr *s )
+endxmlin_datar( xml *node, str *s )
 {
 	int status;
-	if ( node->value && node->value->len ) {
-		newstr_strcat( s, node->value->data );
-		if ( newstr_memerr( s ) ) return BIBL_ERR_MEMERR;
+
+	if ( str_has_value( node->value ) ) {
+		str_strcat( s, node->value );
+		if ( str_memerr( s ) ) return BIBL_ERR_MEMERR;
 	}
 	if ( node->down && xml_tagexact( node->down, "style" ) ) {
 		status = endxmlin_datar( node->down, s );
@@ -154,6 +163,7 @@
 		status = endxmlin_datar( node->next, s );
 		if ( status!=BIBL_OK ) return status;
 	}
+
 	return BIBL_OK;
 }
 
@@ -161,15 +171,19 @@
 endxmlin_data( xml *node, char *inttag, fields *info, int level )
 {
 	int status;
-	newstr s;
-	newstr_init( &s );
+	str s;
+
+	str_init( &s );
+
 	status = endxmlin_datar( node, &s );
 	if ( status!=BIBL_OK ) return status;
-	if ( s.len ) {
+
+	if ( str_has_value( &s ) ) {
 		status = fields_add( info, inttag, s.data, level );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
-	newstr_free( &s );
+
+	str_free( &s );
 	return BIBL_OK;
 }
 
@@ -190,15 +204,15 @@
 		{ "short-title", "SHORTTITLE" },
 	};
 	int i, status, n = sizeof( a ) / sizeof ( a[0] );
-	newstr title;
-	newstr_init( &title );
+	str title;
+	str_init( &title );
 	for ( i=0; i<n; ++i ) {
 		if ( xml_tagexact( node, a[i].attrib ) && node->down ) {
-			newstr_empty( &title );
+			str_empty( &title );
 			status = endxmlin_datar( node, &title );
 			if ( status!=BIBL_OK ) return BIBL_ERR_MEMERR;
-			newstr_trimstartingws( &title );
-			newstr_trimendingws( &title );
+			str_trimstartingws( &title );
+			str_trimendingws( &title );
 			status = fields_add( info, a[i].internal, title.data, 0);
 			if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 		}
@@ -207,7 +221,7 @@
 		status = endxmlin_titles( node->next, info );
 		if ( status!=BIBL_OK ) return status;
 	}
-	newstr_free( &title );
+	str_free( &title );
 	return BIBL_OK;
 }
 
@@ -410,13 +424,14 @@
 endxmlin_reftype( xml *node, fields *info )
 {
 	int status;
-	newstr *s;
+	str *s;
+
 	s = xml_getattrib( node, "name" );
-	if ( s && s->len ) {
-		status = fields_add( info, "%0", s->data, 0 );
-		newstr_free( s );
+	if ( str_has_value( s ) ) {
+		status = fields_add( info, "%0", str_cstr( s ), 0 );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
+
 	return BIBL_OK;
 }
 
@@ -523,7 +538,7 @@
 endxmlin_assembleref( xml *node, fields *info )
 {
 	int status;
-	if ( node->tag->len==0 ) {
+	if ( str_is_empty( node->tag ) ) {
 		if ( node->down )
 			return endxmlin_assembleref( node->down, info );
 	} else if ( xml_tagexact( node, "RECORD" ) ) {
@@ -541,8 +556,8 @@
  * this is necessary as the xml format is as nasty and as overloaded
  * as the tags used in the Refer format output
  */
-int
-endxmlin_processf( fields *fin, char *data, char *filename, long nref )
+static int
+endxmlin_processf( fields *fin, char *data, char *filename, long nref, param *pm )
 {
 	int status;
 	xml top;
diff --git a/bibutils/endxmlin.h b/bibutils/endxmlin.h
deleted file mode 100644
--- a/bibutils/endxmlin.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * endxmlin.h
- *
- * Copyright (c) Chris Putnam 2006-2014
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef ENDXMLIN_H
-#define ENDXMLIN_H
-
-#include "newstr.h"
-#include "fields.h"
-#include "reftypes.h"
-#include "bibutils.h"
-
-extern int endxmlin_readf( FILE *fp, char *buf, int bufsize, int *bufpos,
-	newstr *line, newstr *reference, int *fcharset );
-extern int endxmlin_processf( fields *endin, char *p, char *filename, long nref );
-
-extern void endxmlin_initparams( param *p, const char *progname );
-
-#endif
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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * 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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * 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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Source code released under the GPL version 2
  *
@@ -36,8 +36,8 @@
 	int i;
 
 	for ( i=0; i<f->max; ++i ) {
-		newstr_free( &(f->tag[i]) );
-		newstr_free( &(f->data[i]) );
+		str_free( &(f->tag[i]) );
+		str_free( &(f->data[i]) );
 	}
 	if ( f->tag )   free( f->tag );
 	if ( f->data )  free( f->data );
@@ -52,8 +52,8 @@
 {
 	int i, alloc = 20;
 
-	f->tag   = (newstr *) malloc( sizeof(newstr) * alloc );
-	f->data  = (newstr *) malloc( sizeof(newstr) * alloc );
+	f->tag   = (str *) malloc( sizeof(str) * alloc );
+	f->data  = (str *) malloc( sizeof(str) * alloc );
 	f->used  = (int *)    calloc( alloc, sizeof(int) );
 	f->level = (int *)    calloc( alloc, sizeof(int) );
 	if ( !f->tag || !f->data || !f->used || !f->level ){
@@ -68,8 +68,8 @@
 	f->max = alloc;
 	f->n = 0;
 	for ( i=0; i<alloc; ++i ) {
-		newstr_init( &(f->tag[i]) );
-		newstr_init( &(f->data[i]) );
+		str_init( &(f->tag[i]) );
+		str_init( &(f->data[i]) );
 	}
 	return FIELDS_OK;
 }
@@ -77,12 +77,12 @@
 static int
 fields_realloc( fields *f )
 {
-	newstr *newtags, *newdata;
+	str *newtags, *newdata;
 	int *newused, *newlevel;
 	int i, alloc = f->max * 2;
 
-	newtags = (newstr*) realloc( f->tag, sizeof(newstr) * alloc );
-	newdata = (newstr*) realloc( f->data, sizeof(newstr) * alloc );
+	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 );
 
@@ -97,17 +97,17 @@
 	f->max = alloc;
 
 	for ( i=f->n; i<alloc; ++i ) {
-		newstr_init( &(f->tag[i]) );
-		newstr_init( &(f->data[i]) );
+		str_init( &(f->tag[i]) );
+		str_init( &(f->data[i]) );
 	}
 
 	return FIELDS_OK;
 }
 
 int
-fields_add( fields *f, char *tag, char *data, int level )
+_fields_add( fields *f, char *tag, char *data, int level, int mode )
 {
-	int i, status, found;
+	int i, n, status;
 
 	if ( !tag || !data ) return FIELDS_OK;
 
@@ -119,38 +119,42 @@
 		if ( status!=FIELDS_OK ) return status;
 	}
 
-	/* Don't duplicate identical entries */
-	found = 0;
-	for ( i=0; i<f->n && !found; ++i ) {
-		if ( f->level[i]==level &&
-		     !strcasecmp( f->tag[i].data, tag ) &&
-		     !strcasecmp( f->data[i].data, data ) ) found = 1;
-	}
-	if ( !found ) {
-		f->used[ f->n ]  = 0;
-		f->level[ f->n ] = level;
-		newstr_strcpy( &(f->tag[f->n]), tag );
-		newstr_strcpy( &(f->data[f->n]), data );
-		if ( newstr_memerr( &(f->tag[f->n]) ) ||
-		     newstr_memerr( &(f->data[f->n] ) ) )
-			return FIELDS_ERR;
-		f->n++;
+	/* Don't duplicate identical entries if FIELDS_NO_DUPS */
+	if ( mode == FIELDS_NO_DUPS ) {
+		for ( i=0; i<f->n; i++ ) {
+			if ( f->level[i]==level &&
+			     !strcasecmp( f->tag[i].data, tag ) &&
+			     !strcasecmp( f->data[i].data, data ) )
+				return FIELDS_OK;
+		}
 	}
+
+	n = f->n;
+	f->used[ n ]  = 0;
+	f->level[ n ] = level;
+	str_strcpyc( &(f->tag[n]), tag );
+	str_strcpyc( &(f->data[n]), data );
+
+	if ( str_memerr( &(f->tag[n]) ) || str_memerr( &(f->data[n] ) ) )
+		return FIELDS_ERR;
+
+	f->n++;
+
 	return FIELDS_OK;
 }
 
 int
-fields_add_tagsuffix( fields *f, char *tag, char *suffix,
-		char *data, int level )
+_fields_add_tagsuffix( fields *f, char *tag, char *suffix,
+		char *data, int level, int mode )
 {
-	newstr newtag;
+	str newtag;
 	int ret;
 
-	newstr_init( &newtag );
-	newstr_mergestrs( &newtag, tag, suffix, NULL );
-	if ( newstr_memerr( &newtag ) ) ret = FIELDS_ERR;
-	else ret = fields_add( f, newtag.data, data, level );
-	newstr_free( &newtag );
+	str_init( &newtag );
+	str_mergestrs( &newtag, tag, suffix, NULL );
+	if ( str_memerr( &newtag ) ) ret = FIELDS_ERR;
+	else ret = _fields_add( f, newtag.data, data, level, mode );
+	str_free( &newtag );
 
 	return ret;
 }
@@ -161,7 +165,7 @@
  *
  * level==LEVEL_ANY is a special flag meaning any level can match
  */
-inline int
+int
 fields_match_level( fields *f, int n, int level )
 {
 	if ( level==LEVEL_ANY ) return 1;
@@ -174,28 +178,28 @@
  * returns 1 if tag matches, 0 if not
  *
  */
-inline int
+int
 fields_match_tag( fields *info, int n, char *tag )
 {
 	if ( !strcmp( fields_tag( info, n, FIELDS_CHRP ), tag ) ) return 1;
 	return 0;
 }
 
-inline int
+int
 fields_match_casetag( fields *info, int n, char *tag )
 {
 	if ( !strcasecmp( fields_tag( info, n, FIELDS_CHRP ), tag ) ) return 1;
 	return 0;
 }
 
-inline int
+int
 fields_match_tag_level( fields *info, int n, char *tag, int level )
 {
 	if ( !fields_match_level( info, n, level ) ) return 0;
 	return fields_match_tag( info, n, tag );
 }
 
-inline int
+int
 fields_match_casetag_level( fields *info, int n, char *tag, int level )
 {
 	if ( !fields_match_level( info, n, level ) ) return 0;
@@ -268,35 +272,44 @@
 	int n = fields_find( f, tag, level );
 	if ( n==-1 ) return fields_add( f, tag, data, level );
 	else {
-		newstr_strcpy( &(f->data[n]), data );
-		if ( newstr_memerr( &(f->data[n]) ) ) return FIELDS_ERR;
+		str_strcpyc( &(f->data[n]), data );
+		if ( str_memerr( &(f->data[n]) ) ) return FIELDS_ERR;
 		return FIELDS_OK;
 	}
 }
 
 char *fields_null_value = "\0";
 
-inline int
+int
 fields_used( fields *f, int n )
 {
 	if ( n >= 0 && n < f->n ) return f->used[n];
 	else return 0;
 }
 
-inline int
+int
+fields_notag( fields *f, int n )
+{
+	str *t;
+	if ( n >= 0 && n < f->n ) {
+		t = &( f->tag[n] );
+		if ( t->len > 0 ) return 0;
+	}
+	return 1;
+}
+
+int
 fields_nodata( fields *f, int n )
 {
-	newstr *d;
+	str *d;
 	if ( n >= 0 && n < f->n ) {
 		d = &( f->data[n] );
 		if ( d->len > 0 ) return 0;
-		return 1;
-	} else {
-		return 1;
 	}
+	return 1;
 }
 
-inline int
+int
 fields_num( fields *f )
 {
 	return f->n;
@@ -311,7 +324,7 @@
  * If the length of the tagged value is zero and the mode is
  * FIELDS_STRP_NOLEN or FIELDS_CHRP_NOLEN, return a pointer to
  * a static null string as the data field could be new due to
- * the way newstr handles initialized strings with no data.
+ * the way str handles initialized strings with no data.
  *
  */
 
@@ -504,3 +517,20 @@
 
 	vplist_free( &tags );
 }
+
+void
+fields_report( fields *f, FILE *fp )
+{
+	int i, n;
+	n = fields_num( f );
+	fprintf( fp, "# NUM   level = LEVEL   'TAG' = 'VALUE'\n" );
+	for ( i=0; i<n; ++i ) {
+		fprintf( stderr, "%d\tlevel = %d\t'%s' = '%s'\n",
+			i+1,
+			fields_level( f, i ),
+			(char*)fields_tag( f, i, FIELDS_CHRP_NOUSE ),
+			(char*)fields_value( f, i, FIELDS_CHRP_NOUSE )
+		);
+	}
+}
+
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-2014
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Source code released under the GPL version 2
  *
@@ -20,41 +20,52 @@
 #define LEVEL_ORIG (-2)
 
 #include <stdarg.h>
-#include "newstr.h"
+#include "str.h"
 #include "vplist.h"
 
-typedef struct {
-	newstr    *tag;
-	newstr    *data;
+typedef struct fields {
+	str       *tag;
+	str       *data;
 	int       *used;
 	int       *level;
 	int       n;
 	int       max;
 } fields;
 
-extern void fields_init( fields *f );
-extern fields *fields_new( void );
-extern void fields_free( fields *f );
+void    fields_init( fields *f );
+fields *fields_new( void );
+void    fields_free( fields *f );
 
-extern int  fields_add( fields *f, char *tag, char *data, int level );
-extern int  fields_add_tagsuffix( fields *f, char *tag, char *suffix,
-		char *data, int level );
+#define FIELDS_CAN_DUP (0)
+#define FIELDS_NO_DUPS (1)
 
-extern int  fields_maxlevel( fields *f );
-extern void fields_clearused( fields *f );
-extern void fields_setused( fields *f, int n );
-extern int  fields_replace_or_add( fields *f, char *tag, char *data, int level );
+#define fields_add( a, b, c, d )         _fields_add( a, b, c, d, FIELDS_NO_DUPS )
+#define fields_add_can_dup( a, b, c, d ) _fields_add( a, b, c, d, FIELDS_CAN_DUP )
+#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 )
 
-extern inline int fields_num( fields *f );
-extern inline int fields_used( fields *f, int n );
-extern inline int fields_nodata( fields *f, int n );
+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 );
 
-extern inline int fields_match_level( fields *f, int n, int level );
-extern inline int fields_match_tag( fields *f, int n, char *tag );
-extern inline int fields_match_casetag( fields *f, int n, char *tag );
-extern inline int fields_match_tag_level( fields *f, int n, char *tag, int level );
-extern inline int fields_match_casetag_level( fields *f, int n, char *tag, int level );
+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_num( fields *f );
+int fields_used( fields *f, int n );
+int fields_notag( fields *f, int n );
+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 );
+
+void fields_report( fields *f, FILE *fp );
+
 #define FIELDS_STRP_FLAG     (2)
 #define FIELDS_POSP_FLAG     (4)
 #define FIELDS_NOLENOK_FLAG  (8)
@@ -69,16 +80,16 @@
 #define FIELDS_CHRP_NOUSE  (                            0                              )
 #define FIELDS_STRP_NOUSE  (                     FIELDS_STRP_FLAG                      )
 
-extern void *fields_tag( fields *f, int n, int mode );
-extern void *fields_value( fields *f, int n, int mode );
-extern int   fields_level( fields *f, int n );
+void *fields_tag( fields *f, int n, int mode );
+void *fields_value( fields *f, int n, int mode );
+int   fields_level( fields *f, int n );
  
-extern int   fields_find( fields *f, char *searchtag, int level );
+int   fields_find( fields *f, char *searchtag, int level );
 
-extern void *fields_findv( fields *f, int level, int mode, char *tag );
-extern void *fields_findv_firstof( fields *f, int level, int mode, ... );
+void *fields_findv( fields *f, int level, int mode, char *tag );
+void *fields_findv_firstof( fields *f, int level, int mode, ... );
 
-extern void  fields_findv_each( fields *f, int level, int mode, vplist *a, char *tag );
-extern void  fields_findv_eachof( fields *f, int level, int mode, vplist *a, ... );
+void  fields_findv_each( fields *f, int level, int mode, vplist *a, char *tag );
+void  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-2013
+ * Copyright (c) Chris Putnam 2008-2017
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/generic.c b/bibutils/generic.c
new file mode 100644
--- /dev/null
+++ b/bibutils/generic.c
@@ -0,0 +1,81 @@
+/* generic.c
+ *
+ * Copyright (c) Chris Putnam 2016-2017
+ *
+ * Source code released under GPL version 2
+ *
+ * xxxx_convertf() stubs that can be shared.
+ */
+#include "name.h"
+#include "notes.h"
+#include "pages.h"
+#include "serialno.h"
+#include "title.h"
+#include "url.h"
+#include "generic.h"
+
+/* stub for processtypes that aren't used, such as DEFAULT and ALWAYS handled by bibcore.c  */
+int
+generic_null( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	return BIBL_OK;
+}
+
+int
+generic_url( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	return urls_split_and_add( invalue->data, bibout, level );
+}
+
+int
+generic_notes( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+        if ( notes_add( bibout, invalue, level ) ) return BIBL_OK;
+        else return BIBL_ERR_MEMERR;
+}
+
+int
+generic_pages( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+        if ( pages_add( bibout, outtag, invalue, level ) ) return BIBL_OK;
+        else return BIBL_ERR_MEMERR;
+}
+
+int
+generic_person( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+        if ( name_add( bibout, outtag, invalue->data, level, &(pm->asis), &(pm->corps) ) ) return BIBL_OK;
+        else return BIBL_ERR_MEMERR;
+}
+
+int
+generic_serialno( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	if ( addsn( bibout, invalue->data, level ) ) return BIBL_OK;
+	return BIBL_ERR_MEMERR;
+}
+
+/* SIMPLE = just copy */
+int
+generic_simple( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	if ( fields_add( bibout, outtag, invalue->data, level ) == FIELDS_OK ) return BIBL_OK;
+	else return BIBL_ERR_MEMERR;
+}
+
+/* just like generic_null(), but useful if we need one that isn't identical to generic_null() ala biblatexin.c */
+int
+generic_skip( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	fields_setused( bibin, n );
+	return BIBL_OK;
+}
+
+int
+generic_title( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+        if ( title_process( bibout, outtag, invalue->data, level, pm->nosplittitle ) ) return BIBL_OK;
+        else return BIBL_ERR_MEMERR;
+}
+
+
diff --git a/bibutils/generic.h b/bibutils/generic.h
new file mode 100644
--- /dev/null
+++ b/bibutils/generic.h
@@ -0,0 +1,23 @@
+/* generic.h
+ *
+ * Copyright (c) Chris Putnam 2016-2017
+ *
+ * Source code released under GPL version 2
+ *
+ */
+#ifndef GENERIC_H
+#define GENERIC_H
+
+#include "bibutils.h"
+
+int generic_null    ( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout );
+int generic_url     ( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout );
+int generic_notes   ( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout );
+int generic_pages   ( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout );
+int generic_person  ( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout );
+int generic_serialno( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout );
+int generic_simple  ( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout );
+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 );
+
+#endif
diff --git a/bibutils/intlist.c b/bibutils/intlist.c
--- a/bibutils/intlist.c
+++ b/bibutils/intlist.c
@@ -1,79 +1,142 @@
 /*
  * intlist.c
  *
- * Copyright (c) Chris Putnam 2007-2013
+ * Copyright (c) Chris Putnam 2007-2017
  *
- * Version 9/5/2013
+ * Version 1/12/2017
  *
  * Source code released under the GPL version 2
  *
  * Implements a simple managed array of ints
  *
  */
+#include <stdlib.h>
+#include <assert.h>
 #include "intlist.h"
 
+#define INTLIST_MINALLOC (20)
+
 static int
-intlist_alloc( intlist *il )
+intlist_validn( intlist *il, int n )
 {
-	int alloc = 20, i;
-	il->data = ( int * ) malloc( sizeof( int ) * alloc );
-	if ( !(il->data) ) return 0;
-	for ( i=0; i<alloc; ++i )
-		il->data[i] = 0;
-	il->max = alloc;
-	il->n = 0;
+	if ( n < 0 || n >= il->n ) return 0;
 	return 1;
 }
 
+int
+intlist_wasfound( intlist *il, int n )
+{
+	if ( n!=-1 ) return 1;
+	else return 0;
+}
+
+int
+intlist_wasnotfound( intlist *il, int n )
+{
+	if ( n==-1 ) return 1;
+	else return 0;
+}
+
 static int
-intlist_realloc( intlist *il )
+intlist_alloc( intlist *il, int alloc_size )
 {
-	int *more;
-	int alloc = il->max * 2, i;
-	more = ( int * ) realloc( il->data, sizeof( int ) * alloc );
-	if ( !more ) return 0;
+	il->data = ( int * ) calloc( alloc_size, sizeof( int ) );
+	if ( !(il->data) ) return INTLIST_MEMERR;
+	il->max = alloc_size;
+	il->n   = 0;
+	return INTLIST_OK;
+}
+
+static int
+intlist_realloc( intlist *il, int alloc_size )
+{
+	int i, *more;
+
+	more = ( int * ) realloc( il->data, sizeof( int ) * alloc_size );
+	if ( !more ) return INTLIST_MEMERR;
+
 	il->data = more;
-	for ( i=il->max; i<alloc; ++i )
+	il->max  = alloc_size;
+
+	for ( i=il->max; i<alloc_size; ++i )
 		il->data[i] = 0;
-	il->max = alloc;
-	return 1;
+
+	return INTLIST_OK;
 }
 
+static int
+intlist_ensure_space( intlist *il, int n )
+{
+	int alloc = n;
+
+	if ( il->max == 0 ) {
+		if ( alloc < INTLIST_MINALLOC ) alloc = INTLIST_MINALLOC;
+		return intlist_alloc( il, alloc );
+	}
+
+	else if ( il->max <= n ) {
+		if ( alloc < il->max * 2 ) alloc = il->max * 2;
+		return intlist_realloc( il, alloc );
+	}
+
+	return INTLIST_OK;
+}
+
 /* intlist_add()
  *
- * Returns position of newly added value in range [0,n), -1 on error
+ * Returns INTLIST_OK/INTLIST_MEMERR
  */
 int
 intlist_add( intlist *il, int value )
 {
-	if ( il->max==0 ) {
-		if ( !intlist_alloc( il ) ) return -1;
-	} else if ( il->n >= il->max ) {
-		if ( !intlist_realloc( il ) ) return -1;
+	int status;
+
+	assert( il );
+
+	status = intlist_ensure_space( il, il->n+1 );
+
+	if ( status == INTLIST_OK ) {
+		il->data[ il->n ] = value;
+		il->n++;
 	}
-	il->data[ il->n ] = value;
-	il->n++;
-	return il->n - 1;
+
+	return status;
 }
 
 /* intlist_add_unique()
  *
- * Returns position of newly added (or previously added) value
- * Returns -1 on (memory) error
+ * Returns INTLIST_OK/INTLIST_MEMERR
  */
 int
 intlist_add_unique( intlist *il, int value )
 {
 	int n;
+
+	assert( il );
+
 	n = intlist_find( il, value );
-	if ( n==-1 )
-		n = intlist_add( il, value );
-	return n;
+	if ( intlist_wasnotfound( il, n ) )
+		return intlist_add( il, value );
+	else
+		return INTLIST_OK;
 }
+
 int
 intlist_find_or_add( intlist *il, int value )
 {
-	return intlist_add_unique( il, value );
+	int n, status;
+
+	n = intlist_find( il, value );
+
+	if ( intlist_wasfound( il, n ) ) {
+		return n;
+	}
+
+	else {
+		status = intlist_add( il, value );
+		if ( status!=INTLIST_OK ) return -1;
+		else return il->n - 1;
+	}
 }
 
 /* intlist_find()
@@ -85,56 +148,74 @@
 intlist_find( intlist *il, int value )
 {
 	int i;
+
+	assert( il );
+
 	for ( i=0; i<il->n; ++i )
 		if ( il->data[i]==value ) return i;
+
 	return -1;
 }
 
-static void
+static int
 intlist_remove_pos_core( intlist *il, int pos )
 {
 	int i;
+
+	assert( il );
+
 	for ( i=pos; i<il->n-1; ++i )
 		il->data[i] = il->data[i+1];
 	il->n -= 1;
+
+	return INTLIST_OK;
 }
 
 /* intlist_remove_pos()
  *
- * Returns 1 on success, 0 on failure.
+ * Returns INTLIST_OK on success.
  */
 int
 intlist_remove_pos( intlist *il, int pos )
 {
-	if ( pos < 0 || pos >= il->n ) return 0;
-	intlist_remove_pos_core( il, pos );
-	return 1;
+	assert( il );
+	assert( intlist_validn( il, pos ) );
+
+	return intlist_remove_pos_core( il, pos );
 }
 
 /* intlist_remove()
  *
  * Removes first instance of value from the intlist.
- * Returns 1 on success, 0 on error.
+ * Returns INTLIST_OK/INTLIST_VALUE_MISSING
  */
 int
 intlist_remove( intlist *il, int value )
 {
-	int pos = intlist_find( il, value );
-	if ( pos==-1 ) return 0;
-	intlist_remove_pos_core( il, pos );
-	return 1;
+	int pos;
+
+	assert( il );
+
+	pos = intlist_find( il, value );
+	if ( pos==-1 ) return INTLIST_VALUE_MISSING;
+
+	return intlist_remove_pos_core( il, pos );
 }
 
 /* don't actually free space, just reset counter */
 void
 intlist_empty( intlist *il )
 {
+	assert( il );
+
 	il->n = 0;
 }
 
 void
 intlist_free( intlist *il )
 {
+	assert( il );
+
 	if ( il->data ) free( il->data );
 	intlist_init( il );
 }
@@ -142,33 +223,41 @@
 void
 intlist_delete( intlist *il )
 {
-	intlist_free( il );
+	assert( il );
+
+	if ( il->data ) free( il->data );
 	free( il );
 }
 
 void
 intlist_init( intlist *il  )
 {
+	assert( il );
+
 	il->data = NULL;
 	il->max = 0;
 	il->n = 0;
 }
 
+/* Returns INTLIST_OK/INTLIST_MEMERR
+ */
+int
+intlist_init_fill( intlist *il, int n, int v )
+{
+	intlist_init( il );
+	return intlist_fill( il, n, v );
+}
+
 /* intlist_init_range()
  *
  * Initializes intlist to values from [low,high) with step step.
- * Returns -1 on failure, number of elements on success.
+ * Returns INTLIST_OK/INTLIST_MEMERR.
  */
 int
 intlist_init_range( intlist *il, int low, int high, int step )
 {
-	int i, n;
 	intlist_init( il );
-	for ( i=low; i<high; i+= step ) {
-		n = intlist_add( il, i );
-		if ( n==-1 ) return -1;
-	}
-	return il->n;
+	return intlist_fill_range( il, low, high, step );
 }
 
 /* intlist_new()
@@ -179,25 +268,27 @@
 intlist *
 intlist_new( void )
 {
-	intlist *il = ( intlist * ) malloc( sizeof( intlist ) );
+	intlist *il;
+	il = ( intlist * ) malloc( sizeof( intlist ) );
 	if ( il ) intlist_init( il );
 	return il;
 }
 
 /* intlist_new_range()
  *
- * Allocates a intlist initialized to values from [low,high) with step step.
- * Returns pointer to intlist on success, NULL on memory error;
+ * Allocates a intlist initialized to values from [low,high) in increments of step.
+ * Returns pointer to intlist on success, NULL on memory error.
  */
 intlist *
 intlist_new_range( int low, int high, int step )
 {
 	intlist *il;
-	int n;
-	il = ( intlist * ) malloc( sizeof( intlist ) );
+	int status;
+
+	il = intlist_new();
 	if ( il ) {
-		n = intlist_init_range( il, low, high, step );
-		if ( n==-1 ) {
+		status = intlist_fill_range( il, low, high, step );
+		if ( status==INTLIST_MEMERR ) {
 			intlist_free( il );
 			free( il );
 			il = NULL;
@@ -206,6 +297,96 @@
 	return il;
 }
 
+/* intlist_new_range()
+ *
+ * Allocates a intlist initialized to n elements with value v.
+ * Returns pointer to intlist on success, NULL on memory error.
+ */
+intlist *
+intlist_new_fill( int n, int v )
+{
+	intlist *il;
+	int status;
+
+	il = intlist_new();
+	if ( il ) {
+		status = intlist_fill( il, n, v );
+		if ( status==INTLIST_MEMERR ) {
+			intlist_free( il );
+			free( il );
+			il = NULL;
+		}
+	}
+	return il;
+}
+
+/* intlist_fill()
+ *
+ * Fill an intlist with n elements of value v.
+ *
+ * Returns INTLIST_OK or INTLIST_MEMERR.
+ */
+int
+intlist_fill( intlist *il, int n, int v )
+{
+	int i, status;
+
+	assert ( n > 0 );
+
+	status = intlist_ensure_space( il, n );
+
+	if ( status==INTLIST_OK ) {
+
+		for ( i=0; i<n; ++i )
+			il->data[i] = v;
+
+		il->n = n;
+
+	}
+
+	return status;
+}
+
+/* intlist_fill_range()
+ *
+ * Fill an intlist with the values [low,high) in increments of step
+ *
+ * Returns INTLIST_OK or INTLIST_MEMERR.
+ */
+int
+intlist_fill_range( intlist *il, int low, int high, int step )
+{
+	int i, n, status;
+
+	n = ( high - low ) / step + 1;
+
+	assert ( n > 0 );
+
+	status = intlist_ensure_space( il, n );
+
+	if ( status==INTLIST_OK ) {
+
+		il->n = 0;
+
+		/* ...fill intlist with range */
+		if ( step > 0 ) {
+			for ( i=low; i<high; i+=step ) {
+				il->data[il->n] = i;
+				il->n += 1;
+			}
+		}
+		else {
+			for ( i=low; i>high; i+=step ) {
+				il->data[il->n] = i;
+				il->n += 1;
+			}
+		}
+
+	}
+
+	return status;
+}
+
 static int
 intcomp( const void *v1, const void *v2 )
 {
@@ -219,6 +400,8 @@
 void
 intlist_sort( intlist *il )
 {
+	assert( il );
+
 	qsort( il->data, il->n, sizeof( int ), intcomp );
 }
 
@@ -243,6 +426,9 @@
 intlist_randomize( intlist *il )
 {
 	int i, j;
+
+	assert( il );
+
 	if ( il->n < 2 ) return;
 	for ( i=0; i<il->n; ++i ) {
 		j = randomint( i, il->n );
@@ -251,18 +437,27 @@
 	}
 }
 
-/* Returns 1 on success, 0 on error */
+/* Returns INTLIST_OK/INTLIST_MEMERR */
 int
 intlist_copy( intlist *to, intlist *from )
 {
-	int i;
-	intlist_free( to );
-	to->data = ( int* ) malloc( sizeof( int ) * from->n );
-	if ( !to->data ) return 0;
-	to->n = to->max = from->n;
-	for ( i=0; i<from->n; ++i ) 
-		to->data[i] = from->data[i];
-	return 1;
+	int i, status;
+
+	assert( to );
+	assert( from );
+
+	status = intlist_ensure_space( to, from->n );
+
+	if ( status==INTLIST_OK ) {
+
+		to->n = from->n;
+
+		for ( i=0; i<from->n; ++i )
+			to->data[i] = from->data[i];
+
+	}
+
+	return status;
 }
 
 /* Returns pointer on success, NULL on error */
@@ -270,12 +465,14 @@
 intlist_dup( intlist *il )
 {
 	intlist *l;
-	int n;
+	int status;
 
+	assert( il );
+
 	l = intlist_new();
 	if ( l ) {
-		n = intlist_copy( l, il );
-		if ( !n ) {
+		status = intlist_copy( l, il );
+		if ( status==INTLIST_MEMERR ) {
 			intlist_delete( l );
 			l = NULL;
 		}
@@ -287,49 +484,64 @@
 int
 intlist_append( intlist *to, intlist *from )
 {
-	int i, n, nsave = to->n;
-	for ( i=0; i<from->n; ++i ) {
-		n = intlist_add( to, from->data[i] );
-		if ( n==-1 ) {
-			to->n = nsave;
-			return -1;
-		}
+	int i, status;
+
+	assert( to );
+	assert( from );
+
+	status = intlist_ensure_space( to, to->n + from->n );
+
+	if ( status == INTLIST_OK ) {
+
+		for ( i=0; i<from->n; ++i )
+			to->data[ to->n + i ] = from->data[ i ];
+
+		to->n += from->n;
 	}
-	return to->n;
+
+	return status;
 }
 
 int
 intlist_append_unique( intlist *to, intlist *from )
 {
-	int i, n, nsave = to->n;
+	int i, nsave, status = INTLIST_OK;
+
+	assert( to );
+	assert( from );
+
+	nsave = to->n;
 	for ( i=0; i<from->n; ++i ) {
 		if ( intlist_find( to, from->data[i] )!=-1 ) continue;
-		n = intlist_add( to, from->data[i] );
-		if ( n==-1 ) {
+		status = intlist_add( to, from->data[i] );
+		if ( status==INTLIST_MEMERR ) {
 			to->n = nsave;
-			return -1;
 		}
 	}
-	return to->n;
+	return status;
 }
 
 int
 intlist_get( intlist *il, int pos )
 {
-	if ( pos<0 || pos>=il->n ) return 0;
-	else return il->data[pos];
+	assert( il );
+	assert( intlist_validn( il, pos ) );
+
+	return il->data[pos];
 }
 
 /* intlist_set()
  *
- *   Returns 1 on success, 0 on failure
+ *   Returns INTLIST_OK
  */
 int
 intlist_set( intlist *il, int pos, int value )
 {
-	if ( pos<0 || pos>=il->n ) return 0;
+	assert( il );
+	assert( intlist_validn( il, pos ) );
+
 	il->data[pos] = value;
-	return 1;
+	return INTLIST_OK;
 }
 
 float
@@ -339,6 +551,8 @@
 	float median;
 	int m1, m2;
 
+	assert( il );
+
 	if ( il->n==0 ) return 0.0;
 
 	tmp = intlist_dup( il );
@@ -350,7 +564,7 @@
 		median = intlist_get( tmp, tmp->n / 2 );
 	} else {
 		m1 = intlist_get( tmp, tmp->n / 2 );
-		m2 = intlist_get( tmp, tmp->n / 2 + 1);
+		m2 = intlist_get( tmp, tmp->n / 2 - 1);
 		median = ( m1 + m2 ) / 2.0;
 	}
 
@@ -364,6 +578,8 @@
 {
 	float sum = 0.0;
 	int i;
+
+	assert( il );
 
 	if ( il->n==0 ) return 0.0;
 
diff --git a/bibutils/intlist.h b/bibutils/intlist.h
--- a/bibutils/intlist.h
+++ b/bibutils/intlist.h
@@ -1,9 +1,9 @@
 /*
  * intlist.h
  *
- * Copyright (c) Chris Putnam 2007-2013
+ * Copyright (c) Chris Putnam 2007-2017
  *
- * Version 9/5/2013
+ * Version 01/12/2017
  *
  * Source code released under the GPL version 2
  *
@@ -12,35 +12,41 @@
 #ifndef INTLIST_H
 #define INTLIST_H
 
-#include <stdio.h>
-#include <stdlib.h>
+#define INTLIST_OK            (0)
+#define INTLIST_MEMERR        (-1)
+#define INTLIST_VALUE_MISSING (-2)
 
 typedef struct intlist {
 	int n, max;
 	int *data;
 } intlist;
 
-extern void      intlist_init( intlist *il );
-extern int       intlist_init_range( intlist *il, int low, int high, int step );
-extern intlist * intlist_new( void );
-extern intlist * intlist_new_range( int low, int high, int step );
-extern void      intlist_delete( intlist *il );
-extern void      intlist_sort( intlist *il );
-extern void      intlist_randomize( intlist *il );
-extern int       intlist_add( intlist *il, int value );
-extern int       intlist_add_unique( intlist *il, int value );
-extern int       intlist_find( intlist *il, int searchvalue );
-extern int       intlist_find_or_add( intlist *il, int searchvalue );
-extern void      intlist_empty( intlist *il );
-extern void      intlist_free( intlist *il );
-extern int       intlist_copy( intlist *to, intlist *from );
-extern int       intlist_get( intlist *il, int pos );
-extern int       intlist_set( intlist *il, int pos, int value );
-extern int       intlist_remove( intlist *il, int searchvalue );
-extern int       intlist_remove_pos( intlist *il, int pos );
-extern int       intlist_append( intlist *to, intlist *from );
-extern int       intlist_append_unique( intlist *to, intlist *from );
-extern float     intlist_median( intlist *il );
-extern float     intlist_mean( intlist *il );
+void      intlist_init( intlist *il );
+int       intlist_init_fill( intlist *il, int n, int value );
+int       intlist_init_range( intlist *il, int low, int high, int step );
+intlist * intlist_new( void );
+intlist * intlist_new_fill( int n, int value );
+intlist * intlist_new_range( int low, int high, int step );
+void      intlist_delete( intlist *il );
+void      intlist_sort( intlist *il );
+void      intlist_randomize( intlist *il );
+int       intlist_add( intlist *il, int value );
+int       intlist_add_unique( intlist *il, int value );
+int       intlist_fill( intlist *il, int n, int value );
+int       intlist_fill_range( intlist *il, int low, int high, int step );
+int       intlist_find( intlist *il, int searchvalue );
+int       intlist_find_or_add( intlist *il, int searchvalue );
+void      intlist_empty( intlist *il );
+void      intlist_free( intlist *il );
+int       intlist_copy( intlist *to, intlist *from );
+intlist * intlist_dup( intlist *from );
+int       intlist_get( intlist *il, int pos );
+int       intlist_set( intlist *il, int pos, int value );
+int       intlist_remove( intlist *il, int searchvalue );
+int       intlist_remove_pos( intlist *il, int pos );
+int       intlist_append( intlist *to, intlist *from );
+int       intlist_append_unique( intlist *to, intlist *from );
+float     intlist_median( intlist *il );
+float     intlist_mean( intlist *il );
 
 #endif
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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Source code released under the GPL version 2
  *
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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Source code released under the GPL version 2
  *
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-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -10,15 +10,23 @@
 #include <stdlib.h>
 #include <string.h>
 #include "is_ws.h"
-#include "newstr.h"
-#include "newstr_conv.h"
-#include "fields.h"
+#include "str.h"
+#include "str_conv.h"
 #include "name.h"
-#include "title.h"
-#include "serialno.h"
+#include "fields.h"
 #include "reftypes.h"
-#include "isiin.h"
+#include "bibformats.h"
+#include "generic.h"
 
+extern variants isi_all[];
+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_convertf( fields *isiin, fields *info, int reftype, param *p );
+static int isiin_processf( fields *isiin, char *p, char *filename, long nref, param *pm );
+
+
 /*****************************************************
  PUBLIC: void isiin_initparams()
 *****************************************************/
@@ -44,8 +52,8 @@
 	p->all      = isi_all;
 	p->nall     = isi_nall;
 
-	list_init( &(p->asis) );
-	list_init( &(p->corps) );
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
 
 	if ( !progname ) p->progname = NULL;
 	else p->progname = strdup( progname );
@@ -60,7 +68,7 @@
  *   char 2 = uppercase alphabetic character or digit
  */
 static int
-isiin_istag( char *buf )
+is_isi_tag( char *buf )
 {
 	if ( ! (buf[0]>='A' && buf[0]<='Z') ) return 0;
 	if ( ! (((buf[1]>='A' && buf[1]<='Z'))||(buf[1]>='0'&&buf[1]<='9')))
@@ -69,14 +77,14 @@
 }
 
 static int
-readmore( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line )
+readmore( FILE *fp, char *buf, int bufsize, int *bufpos, str *line )
 {
 	if ( line->len ) return 1;
-	else return newstr_fget( fp, buf, bufsize, bufpos, line );
+	else return str_fget( fp, buf, bufsize, bufpos, line );
 }
 
-int
-isiin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset )
+static int
+isiin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
 	int haveref = 0, inref = 0;
 	char *p;
@@ -93,7 +101,7 @@
 			p += 3;
 		}
 		/* Each reference ends with 'ER ' */
-		if ( isiin_istag( p ) ) {
+		if ( is_isi_tag( p ) ) {
 			if ( !strncmp( p, "FN ", 3 ) ) {
 				if (strncasecmp( p, "FN ISI Export Format",20)){
 					fprintf( stderr, ": warning file FN type not '%s' not recognized.\n", /*r->progname,*/ p );
@@ -104,20 +112,20 @@
 				}
 			} else if ( !strncmp( p, "ER", 2 ) ) haveref = 1;
 			else {
-				newstr_addchar( reference, '\n' );
-				newstr_strcat( reference, p );
+				str_addchar( reference, '\n' );
+				str_strcatc( reference, p );
 				inref = 1;
 			}
-			newstr_empty( line );
+			str_empty( line );
 		}
 		/* not a tag, but we'll append to the last values */
 		else if ( inref ) {
-			newstr_addchar( reference, '\n' );
-			newstr_strcat( reference, p );
-			newstr_empty( line );
+			str_addchar( reference, '\n' );
+			str_strcatc( reference, p );
+			str_empty( line );
 		}
 		else {
-			newstr_empty( line );
+			str_empty( line );
 		}
 	}
 	return haveref;
@@ -128,114 +136,159 @@
 *****************************************************/
 
 static char *
-process_isiline( newstr *tag, newstr *data, char *p )
+process_tagged_line( str *tag, str *data, char *p )
 {
 	int i;
 
 	/* collect tag and skip past it */
 	i = 0;
 	while ( i<2 && *p && *p!='\r' && *p!='\n') {
-		newstr_addchar( tag, *p++ );
+		str_addchar( tag, *p++ );
 		i++;
 	}
 	while ( *p==' ' || *p=='\t' ) p++;
 	while ( *p && *p!='\r' && *p!='\n' )
-		newstr_addchar( data, *p++ );
-	newstr_trimendingws( data );
+		str_addchar( data, *p++ );
+	str_trimendingws( data );
 	while ( *p=='\r' || *p=='\n' ) p++;
 	return p;
 }
 
-int
-isiin_processf( fields *isiin, char *p, char *filename, long nref )
+static char *
+process_untagged_line( str *data, char *p )
 {
-	int status, n, ret = 1;
-	newstr tag, data;
-	newstrs_init( &tag, &data, NULL );
+	while ( *p==' ' || *p=='\t' ) p++;
+	while ( *p && *p!='\r' && *p!='\n' )
+		str_addchar( data, *p++ );
+	str_trimendingws( data );
+	while ( *p=='\r' || *p=='\n' ) p++;
+	return p;
+}
+
+static int
+add_tag_value( fields *isiin, str *tag, str *value, int *tag_added )
+{
+	int status;
+
+	if ( str_has_value( value ) ) {
+		status = fields_add( isiin, str_cstr( tag ), str_cstr( value ), 0 );
+		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+		*tag_added = 1;
+	}
+
+	else {
+		*tag_added = 0;
+	}
+
+	return BIBL_OK;
+}
+
+static int
+merge_tag_value( fields *isiin, str *tag, str *value, int *tag_added )
+{
+	int n, status;
+
+	if ( str_has_value( value ) ) {
+
+		if ( *tag_added==1 ) {
+
+			n = fields_num( isiin );
+			if ( n==0 ) return BIBL_OK;
+
+			/* only one AU or AF for list of authors */
+			if ( !strcmp( str_cstr( tag ), "AU" ) ) {
+				status = fields_add( isiin, "AU", str_cstr( value ), 0 );
+				if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+			} else if ( !strcmp( str_cstr( tag ), "AF" ) ) {
+				status = fields_add( isiin, "AF", str_cstr( value ), 0 );
+				if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+			}
+			/* otherwise append multiline data */
+			else {
+				str_addchar( &(isiin->data[n-1]),' ');
+				str_strcat( &(isiin->data[n-1]), value );
+				if ( str_memerr( &(isiin->data[n-1]) ) ) return BIBL_ERR_MEMERR;
+			}
+		}
+
+		else {
+                        status = fields_add( isiin, str_cstr( tag ), str_cstr( value ), 0 );
+                        if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+                        *tag_added = 1;
+		}
+	}
+
+	return BIBL_OK;
+}
+
+static int
+isiin_processf( fields *isiin, char *p, char *filename, long nref, param *pm )
+{
+	int status, tag_added = 0, ret = 1;
+	str tag, value;
+
+	strs_init( &tag, &value, NULL );
+
 	while ( *p ) {
-		newstrs_empty( &tag, &data, NULL );
-		p = process_isiline( &tag, &data, p );
-		if ( !data.len ) continue;
-		if ( (tag.len>1) && isiin_istag( tag.data ) ) {
-			status = fields_add( isiin, tag.data, data.data, 0 );
-			if ( status!=FIELDS_OK ) {
+
+		/* ...with tag, add */
+		if ( is_isi_tag( p ) ) {
+			str_empty( &tag );
+			str_empty( &value );
+			p = process_tagged_line( &tag, &value, p );
+			status = add_tag_value( isiin, &tag, &value, &tag_added );
+			if ( status!=BIBL_OK ) {
 				ret = 0;
 				goto out;
 			}
-		} else {
-			n = fields_num( isiin );
-			if ( n>0 ) {
-				/* only one AU or AF for list of authors */
-				if ( !strcmp( isiin->tag[n-1].data,"AU") ){
-					status = fields_add( isiin, "AU", data.data, 0);
-					if ( status!=FIELDS_OK ) ret = 0;
-				} else if ( !strcmp( isiin->tag[n-1].data,"AF") ){
-					status = fields_add( isiin, "AF", data.data, 0);
-					if ( status!=FIELDS_OK ) ret = 0;
-				}
-				/* otherwise append multiline data */
-				else {
-					newstr_addchar( &(isiin->data[n-1]),' ');
-					newstr_strcat( &(isiin->data[n-1]), data.data );
-					if ( newstr_memerr( &(isiin->data[n-1]) ) ) ret = 0;
-				}
-				if ( ret==0 ) goto out;
+		}
+
+		/* ...untagged, merge -- one AU or AF for list of authors */
+		else {
+			str_empty( &value );
+			p = process_untagged_line( &value, p );
+			status = merge_tag_value( isiin, &tag, &value, &tag_added );
+			if ( status!=BIBL_OK ) {
+				ret = 0;
+				goto out;
 			}
 		}
+
 	}
 out:
-	newstrs_free( &data, &tag, NULL );
+	strs_free( &value, &tag, NULL );
 	return ret;
 }
 
 /*****************************************************
  PUBLIC: int isiin_typef()
 *****************************************************/
-int
-isiin_typef( fields *isiin, char *filename, int nref, param *p, variants *all, int nall )
+static int
+isiin_typef( fields *isiin, char *filename, int nref, param *p )
 {
-	char *refnum = "";
-	int n, reftype, nrefnum;
-	n = fields_find( isiin, "PT", 0 );
-	nrefnum = fields_find ( isiin, "UT", 0 );
-	if ( nrefnum!=-1 ) refnum = isiin->data[nrefnum].data;
-	if ( n!=-1 )
-		reftype = get_reftype( (isiin->data[n]).data, nref, p->progname, all, nall, refnum );
-	else
-		reftype = get_reftype( "", nref, p->progname, all, nall, refnum ); /* default */
-	return reftype;
+	int ntypename, nrefname, is_default;
+	char *refname = "", *typename="";
+
+	ntypename = fields_find( isiin, "PT", LEVEL_MAIN );
+	nrefname  = fields_find( isiin, "UT", LEVEL_MAIN );
+
+	if ( nrefname!=-1 )  refname  = fields_value( isiin, nrefname,  FIELDS_CHRP_NOUSE );
+	if ( ntypename!=-1 ) typename = fields_value( isiin, ntypename, FIELDS_CHRP_NOUSE );
+
+	return get_reftype( typename, nref, p->progname, p->all, p->nall, refname, &is_default, REFTYPE_CHATTY );
 }
 
 /*****************************************************
  PUBLIC: int isiin_convertf(), returns BIBL_OK or BIBL_ERR_MEMERR
 *****************************************************/
 
-static int
-isiin_keyword_process( fields *info, char *newtag, char *p, int level )
-{
-	int fstatus, status = BIBL_OK;
-	newstr keyword;
-	newstr_init( &keyword );
-	while ( *p ) {
-		p = newstr_cpytodelim( &keyword, skip_ws( p ), ";", 1 );
-		if ( newstr_memerr( &keyword ) ) { status = BIBL_ERR_MEMERR; goto out; }
-		if ( keyword.len ) {
-			fstatus = fields_add( info, newtag, keyword.data, level );
-			if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
-		}
-	}
-out:
-	newstr_free( &keyword );
-	return status;
-}
-
 /* pull off authors first--use AF before AU */
 static int
-isiin_addauthors( fields *isiin, fields *info, int reftype, variants *all, int nall, list *asis, list *corps )
+isiin_addauthors( fields *isiin, fields *info, int reftype, variants *all, int nall, slist *asis, slist *corps )
 {
 	char *newtag, *authortype, use_af[]="AF", use_au[]="AU";
 	int level, i, n, has_af=0, has_au=0, nfields, ok;
-	newstr *t, *d;
+	str *t, *d;
 
 	nfields = fields_num( isiin );
 	for ( i=0; i<nfields && has_af==0; ++i ) {
@@ -259,6 +312,27 @@
 	return BIBL_OK;
 }
 
+static int
+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;
+	str keyword;
+
+	str_init( &keyword );
+	while ( *p ) {
+		p = str_cpytodelim( &keyword, skip_ws( p ), ";", 1 );
+		if ( str_memerr( &keyword ) ) { status = BIBL_ERR_MEMERR; goto out; }
+		if ( str_has_value( &keyword ) ) {
+			fstatus = fields_add( bibout, outtag, keyword.data, level );
+			if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
+		}
+	}
+out:
+	str_free( &keyword );
+	return status;
+}
+
 static void
 isiin_report_notag( param *p, char *tag )
 {
@@ -269,79 +343,45 @@
 }
 
 static int
-isiin_simple( fields *info, char *tag, char *value, int level )
+isiin_convertf( fields *bibin, fields *bibout, int reftype, param *p )
 {
-	int fstatus = fields_add( info, tag, value, level );
-	if ( fstatus==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
+	static int (*convertfns[NUM_REFTYPES])(fields *, int, str *, str *, int, param *, char *, fields *) = {
+		[ 0 ... NUM_REFTYPES-1 ] = generic_null,
+		[ SIMPLE       ] = generic_simple,
+		[ TITLE        ] = generic_title,
+		[ PERSON       ] = generic_person,
+		[ SERIALNO     ] = generic_serialno,
+		[ DATE         ] = generic_simple,
+		[ NOTES        ] = generic_notes,
+		[ KEYWORD      ] = isiin_keyword,
+	};
 
-int
-isiin_convertf( fields *isiin, fields *info, int reftype, param *p, variants *all, int nall )
-{
-	int process, level, i, n, nfields, ok, status;
-	newstr *t, *d;
-	char *newtag;
+	int process, level, i, nfields, status;
+	str *intag, *invalue;
+	char *outtag;
 
-	status = isiin_addauthors( isiin, info, reftype, all, nall, &(p->asis), &(p->corps) );
+	status = isiin_addauthors( bibin, bibout, reftype, p->all, p->nall, &(p->asis), &(p->corps) );
 	if ( status!=BIBL_OK ) return status;
 
-	nfields = fields_num( isiin );
+	nfields = fields_num( bibin );
 	for ( i=0; i<nfields; ++i ) {
 
-		t = fields_tag( isiin, i, FIELDS_STRP );
-		if ( !strcasecmp( t->data, "AU" ) || !strcasecmp( t->data, "AF" ) )
+		intag = fields_tag( bibin, i, FIELDS_STRP );
+		if ( !strcasecmp( str_cstr( intag ), "AU" ) || !strcasecmp( str_cstr( intag ), "AF" ) )
 			continue;
 
-		n = translate_oldtag( t->data, reftype, all, nall, &process, &level, &newtag );
-		if ( n==-1 ) {
-			isiin_report_notag( p, t->data );
+		if ( !translate_oldtag( str_cstr( intag ), reftype, p->all, p->nall, &process, &level, &outtag ) ) {
+			isiin_report_notag( p, str_cstr( intag ) );
 			continue;
 		}
-		if ( process == ALWAYS ) continue; /* add in core code */
 
-		d = fields_value( isiin, i, FIELDS_STRP );
-
-		ok = 1;
-
-		switch ( process ) {
-
-		case SIMPLE:
-		case DATE:
-			status = isiin_simple( info, newtag, d->data, level );
-			break;
-
-		case PERSON:
-			ok = name_add( info, newtag, d->data, level, &(p->asis), &(p->corps) );
-			if ( ok ) status = BIBL_OK;
-			else status = BIBL_ERR_MEMERR;
-			break;
-
-		case TITLE:
-			ok = title_process( info, newtag, d->data, level, p->nosplittitle );
-			if ( ok ) status = BIBL_OK;
-			else status = BIBL_ERR_MEMERR;
-			break;
-
-		case KEYWORD:
-			status = isiin_keyword_process( info, newtag, d->data, level );
-			break;
-
-		case SERIALNO:
-			ok = addsn( info, d->data, level );
-			if ( ok ) status = BIBL_OK;
-			else status = BIBL_ERR_MEMERR;
-			break;
-
-		/* do nothing if process==TYPE || process==ALWAYS */
-		default:
-			status = BIBL_OK;
-			break;
-
-		}
+		invalue = fields_value( bibin, i, FIELDS_STRP );
 
+		status = convertfns[ process ] ( bibin, i, intag, invalue, level, p, outtag, bibout );
 		if ( status!=BIBL_OK ) return status;
 	}
+
+	if ( status==BIBL_OK && p->verbose ) fields_report( bibout, stderr );
 
 	return status;
 }
diff --git a/bibutils/isiin.h b/bibutils/isiin.h
deleted file mode 100644
--- a/bibutils/isiin.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * isiin.h
- * 
- * Copyright (c) Chris Putnam 2004-2013
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef ISIIN_H
-#define ISIIN_H
-
-#include "newstr.h"
-#include "fields.h"
-#include "reftypes.h"
-#include "bibutils.h"
-
-extern int isiin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset );
-extern int isiin_typef( fields *isiin, char *filename, int nref, param *p, variants *all, int nall );
-extern int isiin_convertf( fields *isiin, fields *info, int reftype, param *p, variants *all, int nall );
-extern int isiin_processf( fields *isiin, char *p, char *filename, long nref );
-
-extern void isiin_initparams( param *p, const char *progname );
-
-extern variants isi_all[];
-extern int isi_nall;
-
-#endif
-
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-2013
+ * Copyright (c) Chris Putnam 2008-2017
  *
  * Source code released under the GPL version 2
  *
@@ -11,12 +11,16 @@
 #include <string.h>
 #include <ctype.h>
 #include "utf8.h"
-#include "newstr.h"
+#include "str.h"
 #include "strsearch.h"
 #include "fields.h"
+#include "title.h"
 #include "bibutils.h"
-#include "isiout.h"
+#include "bibformats.h"
 
+static int  isiout_write( fields *info, FILE *fp, param *p, unsigned long refnum );
+static void isiout_writeheader( FILE *outptr, param *p );
+
 void
 isiout_initparams( param *p, const char *progname )
 {
@@ -49,29 +53,18 @@
         TYPE_BOOK    = 3,
 };
 
-static void
-output_type( FILE *fp, int type )
-{
-	fprintf( fp, "PT " );
-	if ( type==TYPE_ARTICLE ) fprintf( fp, "Journal" );
-	else if ( type==TYPE_INBOOK ) fprintf( fp, "Chapter" );
-	else if ( type==TYPE_BOOK ) fprintf( fp, "Book" );
-	else fprintf( fp, "Unknown" );
-	fprintf( fp, "\n" );
-}
-
 static int 
-get_type( fields *f )
+get_type( fields *in )
 {
         int type = TYPE_UNKNOWN, i, n, level;
 	char *tag, *value;
-	n = fields_num( f );
+	n = fields_num( in );
         for ( i=0; i<n; ++i ) {
-		tag = fields_tag( f, i, FIELDS_CHRP );
+		tag = fields_tag( in, i, FIELDS_CHRP );
                 if ( strcasecmp( tag, "GENRE" ) &&
                      strcasecmp( tag, "NGENRE") ) continue;
-		value = fields_value( f, i, FIELDS_CHRP );
-		level = fields_level( f, i );
+		value = fields_value( in, i, FIELDS_CHRP );
+		level = fields_level( in, i );
                 if ( !strcasecmp( value, "periodical" ) ||
                      !strcasecmp( value, "academic journal" ) ||
 		     !strcasecmp( value, "journal article" ) ) {
@@ -87,225 +80,297 @@
 }
 
 static void
-output_titlecore( FILE *fp, fields *f, char *isitag, int level,
-	char *maintag, char *subtag )
+append_type( int type, fields *out, int *status )
 {
-	newstr *mainttl = fields_findv( f, level, FIELDS_STRP, maintag );
-	newstr *subttl  = fields_findv( f, level, FIELDS_STRP, subtag );
+	int fstatus;
+	char *s;
 
-	if ( !mainttl ) return;
+	if ( type==TYPE_ARTICLE ) s = "Journal";
+	else if ( type==TYPE_INBOOK ) s = "Chapter";
+	else if ( type==TYPE_BOOK ) s = "Book";
+	else s = "Unknown";
 
-	fprintf( fp, "%s %s", isitag, mainttl->data );
-	if ( subttl ) {
-		if ( mainttl->len > 0 &&
-		     mainttl->data[ mainttl->len - 1 ]!='?' )
-				fprintf( fp, ":" );
-		fprintf( fp, " %s", subttl->data );
+	fstatus = fields_add( out, "PT", s, LEVEL_MAIN );
+	if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+}
+
+static void
+append_titlecore( fields *in, char *isitag, int level, char *maintag, char *subtag, fields *out, int *status )
+{
+	str *mainttl = fields_findv( in, level, FIELDS_STRP, maintag );
+	str *subttl  = fields_findv( in, level, FIELDS_STRP, subtag );
+	str fullttl;
+	int fstatus;
+
+	str_init( &fullttl );
+	title_combine( &fullttl, mainttl, subttl );
+
+	if ( str_memerr( &fullttl ) ) {
+		*status = BIBL_ERR_MEMERR;
+		goto out;
 	}
-	fprintf( fp, "\n" );
+
+	if ( str_has_value( &fullttl ) ) {
+		fstatus = fields_add( out, isitag, str_cstr( &fullttl ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+out:
+	str_free( &fullttl );
 }
 
 static void
-output_title( FILE *fp, fields *f, char *isitag, int level )
+append_title( fields *in, char *isitag, int level, fields *out, int *status )
 {
-	output_titlecore( fp, f, isitag, level, "TITLE", "SUBTITLE" );
+	append_titlecore( in, isitag, level, "TITLE", "SUBTITLE", out, status );
 }
 
 static void
-output_abbrtitle( FILE *fp, fields *f, char *isitag, int level )
+append_abbrtitle( fields *in, char *isitag, int level, fields *out, int *status )
 {
-	output_titlecore( fp, f, isitag, level, "SHORTTITLE", "SHORTSUBTITLE" );
+	append_titlecore( in, isitag, level, "SHORTTITLE", "SHORTSUBTITLE", out, status );
 }
 
 static void
-output_keywords( FILE *fp, fields *f )
+append_keywords( fields *in, fields *out, int *status )
 {
+	vplist_index i;
+	str keywords;
+	int fstatus;
 	vplist kw;
-	int i;
+
+	str_init( &keywords );
 	vplist_init( &kw );
-	fields_findv_each( f, LEVEL_ANY, FIELDS_CHRP, &kw, "KEYWORD" );
+
+	fields_findv_each( in, LEVEL_ANY, FIELDS_STRP, &kw, "KEYWORD" );
 	if ( kw.n ) {
-		fprintf( fp, "DE " );
 		for ( i=0; i<kw.n; ++i ) {
-			if ( i>0 ) fprintf( fp, "; " );
-			fprintf( fp, "%s", (char *)vplist_get( &kw, i ) );
+			if ( i>0 ) str_strcatc( &keywords, "; " );
+			str_strcat( &keywords, (str *) vplist_get( &kw, i ) );
 		}
-		fprintf( fp, "\n" );
+		if ( str_memerr( &keywords ) ) { *status = BIBL_ERR_MEMERR; goto out; }
+		fstatus = fields_add( out, "DE", str_cstr( &keywords ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) { *status = BIBL_ERR_MEMERR; goto out; }
 	}
+out:
 	vplist_free( &kw );
+	str_free( &keywords );
 }
 
 static void
-output_person( FILE *fp, char *name )
+process_person( str *person, char *name )
 {
-	newstr family, given, suffix;
+	str family, given, suffix;
 	char *p = name;
 
-	newstrs_init( &family, &given, &suffix, NULL );
+	str_empty( person );
 
+	strs_init( &family, &given, &suffix, NULL );
+
 	while ( *p && *p!='|' )
-		newstr_addchar( &family, *p++ );
+		str_addchar( &family, *p++ );
 
 	while ( *p=='|' && *(p+1)!='|' ) {
 		p++;
-		if ( *p!='|' ) newstr_addchar( &given, *p++ );
+		if ( *p!='|' ) str_addchar( &given, *p++ );
 		while ( *p && *p!='|' ) p++;
 	}
 
 	if ( *p=='|' && *(p+1)=='|' ) {
 		p += 2;
-		while ( *p && *p!='|' ) newstr_addchar( &suffix, *p++ );
+		while ( *p && *p!='|' ) str_addchar( &suffix, *p++ );
 	}
 
-	if ( family.len ) fprintf( fp, "%s", family.data );
-	if ( suffix.len ) {
-		if ( family.len ) fprintf( fp, " %s", suffix.data );
-		else fprintf( fp, "%s", suffix.data );
+	if ( str_has_value( &family ) ) str_strcat( person, &family );
+	if ( str_has_value( &suffix ) ) {
+		if ( str_has_value( &family ) ) str_strcatc( person, " " );
+		str_strcat( person, &suffix );
 	}
-	if ( given.len ) fprintf( fp, ", %s", given.data );
+	if ( str_has_value( &given ) ) {
+		if ( str_has_value( person ) ) str_strcatc( person, ", " );
+		str_strcat( person, &given );
+	}
 
-	newstrs_free( &family, &given, &suffix, NULL );
+	strs_free( &family, &given, &suffix, NULL );
 }
 
 static void
-output_people( FILE *fp, fields *f, char *tag, char *isitag, int level )
+append_people( fields *f, char *tag, char *isitag, int level, fields *out, int *status )
 {
+	vplist_index i;
 	vplist people;
-	int i;
+	str person;
+	int fstatus;
+
+	str_init( &person );
 	vplist_init( &people );
+
 	fields_findv_each( f, level, FIELDS_CHRP, &people, tag );
-	if ( people.n ) {
-		fprintf( fp, "%s ", isitag );
-		for ( i=0; i<people.n; ++i ) {
-			if ( i!=0 ) fprintf( fp, "   " );
-			output_person( fp, (char *)vplist_get( &people, i ) );
-			fprintf( fp, "\n" );
-		}
+	for ( i=0; i<people.n; ++i ) {
+		process_person( &person, (char *)vplist_get( &people, i ) );
+		if ( str_memerr( &person ) ) { *status = BIBL_ERR_MEMERR; goto out; }
+		if ( i==0 ) fstatus = fields_add_can_dup( out, isitag, str_cstr( &person ), LEVEL_MAIN );
+		else        fstatus = fields_add_can_dup( out, "  ",   str_cstr( &person ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) { *status = BIBL_ERR_MEMERR; goto out; }
 	}
+
+out:
 	vplist_free( &people );
+	str_free( &person );
 }
 
 static void
-output_easy( FILE *fp, fields *f, char *tag, char *isitag, int level )
+append_easy( fields *in, char *tag, char *isitag, int level, fields *out, int *status )
 {
-	char *value = fields_findv( f, level, FIELDS_CHRP, tag );
-	if ( value ) fprintf( fp, "%s %s\n", isitag, value );
+	char *value;
+	int fstatus;
+
+	value = fields_findv( in, level, FIELDS_CHRP, tag );
+	if ( value ) {
+		fstatus = fields_add( out, isitag, value, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 }
 
 static void
-output_easyall( FILE *fp, fields *f, char *tag, char *isitag, int level )
+append_easyall( fields *in, char *tag, char *isitag, int level, fields *out, int *status )
 {
+	vplist_index i;
+	int fstatus;
 	vplist a;
-	int i;
+
 	vplist_init( &a );
-	fields_findv_each( f, level, FIELDS_CHRP, &a, tag );
-	for ( i=0; i<a.n; ++i )
-		fprintf( fp, "%s %s\n", isitag, (char *) vplist_get( &a, i ) );
+	fields_findv_each( in, level, FIELDS_CHRP, &a, tag );
+	for ( i=0; i<a.n; ++i ) {
+		fstatus = fields_add( out, isitag, (char *) vplist_get( &a, i ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 	vplist_free( &a );
 }
 
 static void
-output_date( FILE *fp, fields *f )
+append_date( fields *in, fields *out, int *status )
 {
-	char *month = fields_findv_firstof( f, LEVEL_ANY, FIELDS_CHRP,
-		"PARTMONTH", "MONTH", NULL );
-	char *year  = fields_findv_firstof( f, LEVEL_ANY, FIELDS_CHRP,
-		"PARTYEAR", "YEAR", NULL );
-	if ( month ) fprintf( fp, "PD %s\n", month );
-	if ( year )  fprintf( fp, "PY %s\n", year );
+	char *month, *year;
+	int fstatus;
+
+	month = fields_findv_firstof( in, LEVEL_ANY, FIELDS_CHRP, "PARTDATE:MONTH", "DATE:MONTH", NULL );
+	if ( month ) {
+		fstatus = fields_add( out, "PD", month, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+	year  = fields_findv_firstof( in, LEVEL_ANY, FIELDS_CHRP, "PARTDATE:YEAR",  "DATE:YEAR",  NULL );
+	if ( year ) {
+		fstatus = fields_add( out, "PY", year, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 }
 
+static int
+append_data( fields *in, fields *out )
+{
+	int type, status = BIBL_OK;
+
+	type = get_type( in );
+
+	append_type   ( type, out, &status );
+	append_people ( in, "AUTHOR",      "AU", LEVEL_MAIN, out, &status );
+	append_easyall( in, "AUTHOR:CORP", "AU", LEVEL_MAIN, out, &status );
+	append_easyall( in, "AUTHOR:ASIS", "AU", LEVEL_MAIN, out, &status );
+
+	append_title  ( in, "TI", LEVEL_MAIN, out, &status );
+	if ( type==TYPE_ARTICLE ) {
+		append_title( in, "SO", LEVEL_HOST,   out, &status );
+		append_abbrtitle( in, "JI", LEVEL_HOST, out, &status );
+		append_title( in, "SE", LEVEL_SERIES, out, &status );
+	} else if ( type==TYPE_INBOOK ) {
+		append_title( in, "BT", LEVEL_HOST,   out, &status );
+		append_title( in, "SE", LEVEL_SERIES, out, &status );
+	} else { /* type==BOOK */
+		append_title( in, "SE", LEVEL_HOST,   out, &status );
+	}
+
+	append_date( in, out, &status );
+
+	append_easy( in, "PAGES:START",    "BP", LEVEL_ANY, out, &status );
+	append_easy( in, "PAGES:STOP",     "EP", LEVEL_ANY, out, &status );
+	append_easy( in, "ARTICLENUMBER",  "AR", LEVEL_ANY, out, &status );
+	append_easy( in, "PAGES:TOTAL",    "PG", LEVEL_ANY, out, &status );
+
+	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, "DOI",            "DI", 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, "ABSTRACT",       "AB", LEVEL_ANY, out, &status );
+	append_easy( in, "TIMESCITED",     "TC", LEVEL_ANY, out, &status );
+	append_easy( in, "NUMBERREFS",     "NR", LEVEL_ANY, out, &status );
+	append_easy( in, "CITEDREFS",      "CR", LEVEL_ANY, out, &status );
+	append_easy( in, "ADDRESS",        "PI", LEVEL_ANY, out, &status );
+
+	return status;
+}
+
 static void
-output_verbose( fields *f, unsigned long refnum )
+output_verbose( fields *f, const char *type, unsigned long refnum )
 {
 	char *tag, *value;
 	int i, n, level;
-	fprintf( stderr, "REF #%lu----\n", refnum+1 );
+
+	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 );
+		fprintf( stderr, "\t'%s'\t'%s'\t%d\n", tag, value, level );
 	}
+
+	fflush( stderr );
 }
 
-void
-isiout_write( fields *f, FILE *fp, param *p, unsigned long refnum )
+static void
+output( FILE *fp, fields *out )
 {
-        int type = get_type( f );
+	int i;
 
-	if ( p->format_opts & BIBL_FORMAT_VERBOSE )
-		output_verbose( f, refnum );
+	for ( i=0; i<out->n; ++i ) {
+		fprintf( fp, "%s %s\n",
+			( char * ) fields_tag  ( out, i, FIELDS_CHRP ),
+			( char * ) fields_value( out, i, FIELDS_CHRP )
+		);
+	}
+        fprintf( fp, "ER\n\n" );
+        fflush( fp );
+}
 
-        output_type( fp, type );
-	output_people( fp, f, "AUTHOR", "AU", 0 );
-	output_easyall( fp, f, "AUTHOR:CORP", "AU", 0 );
-	output_easyall( fp, f, "AUTHOR:ASIS", "AU", 0 );
-/*      output_people( fp, f, "AUTHOR", "A2", 1 );
-        output_people( fp, f, "AUTHOR:CORP", "A2", 1 );
-        output_people( fp, f, "AUTHOR:ASIS", "A2", 1 );
-        output_people( fp, f, "AUTHOR", "A3", 2 );
-        output_people( fp, f, "AUTHOR:CORP", "A3", 2 );
-        output_people( fp, f, "AUTHOR:ASIS", "A3", 2 );
-        output_people( fp, f, "EDITOR", "ED", -1 );
-	output_people( fp, f, "EDITOR:CORP", "ED", -1 );
-        output_people( fp, f, "EDITOR:ASIS", "ED", -1 );*/
-/*        output_date( fp, f, refnum );*/
+static int
+isiout_write( fields *in, FILE *fp, param *p, unsigned long refnum )
+{
+	int status;
+	fields out;
 
-        output_title( fp, f, "TI", 0 );
-        if ( type==TYPE_ARTICLE ) {
-		output_title( fp, f, "SO", 1 );
-		output_abbrtitle( fp, f, "JI", 1 );
-		output_title( fp, f, "SE", 2 );
-	} else if ( type==TYPE_INBOOK ) {
-		output_title( fp, f, "BT", 1 );
-		output_title( fp, f, "SE", 2 );
-	} else { /* type==BOOK */
-		output_title( fp, f, "SE", 1 );
-	}
+	fields_init( &out );
 
-	output_date( fp, f );
-/*	output_easy( fp, f, "PARTMONTH", "PD", -1 );
-	output_easy( fp, f, "PARTYEAR", "PY", -1 );*/
+	if ( p->format_opts & BIBL_FORMAT_VERBOSE )
+		output_verbose( in, "IN", refnum );
 
-	output_easy( fp, f, "PAGESTART", "BP", -1 );
-	output_easy( fp, f, "PAGEEND",   "EP", -1 );
-        output_easy( fp, f, "ARTICLENUMBER", "AR", -1 );
-        /* output article number as pages */
-	output_easy( fp, f, "TOTALPAGES","PG", -1 );
+	status = append_data( in, &out );
 
-        output_easy( fp, f, "VOLUME",    "VL", -1 );
-        output_easy( fp, f, "ISSUE",     "IS", -1 );
-        output_easy( fp, f, "NUMBER",    "IS", -1 );
-	output_easy( fp, f, "DOI",       "DI", -1 );
-	output_easy( fp, f, "ISIREFNUM", "UT", -1 );
-	output_easy( fp, f, "LANGUAGE",  "LA", -1 );
-	output_easy( fp, f, "ISIDELIVERNUM", "GA", -1 );
-	output_keywords( fp, f );
-	output_easy( fp, f, "ABSTRACT",  "AB", -1 );
-	output_easy( fp, f, "TIMESCITED", "TC", -1 );
-	output_easy( fp, f, "NUMBERREFS", "NR", -1 );
-	output_easy( fp, f, "CITEDREFS",  "CR", -1 );
-	output_easy( fp, f, "ADDRESS",    "PI", -1 );
+	if ( status==BIBL_OK ) output( fp, &out );
 
-/*        output_easy( fp, f, "PUBLISHER", "PB", -1 );
-        output_easy( fp, f, "DEGREEGRANTOR", "PB", -1 );
-        output_easy( fp, f, "ADDRESS", "CY", -1 );
-        output_easy( fp, f, "ABSTRACT", "AB", -1 );
-        output_easy( fp, f, "ISSN", "SN", -1 );
-        output_easy( fp, f, "ISBN", "SN", -1 );
-        output_easyall( fp, f, "URL", "UR", -1 );
-        output_easyall( fp, f, "FILEATTACH", "UR", -1 );
-        output_pubmed( fp, f, refnum );
-        output_easyall( fp, f, "NOTES", "N1", -1 );
-        output_easyall( fp, f, "REFNUM", "ID", -1 );*/
-        fprintf( fp, "ER\n\n" );
-        fflush( fp );
+	if ( p->format_opts & BIBL_FORMAT_VERBOSE )
+		output_verbose( &out, "OUT", refnum );
+
+	fields_free( &out );
+
+	return status;
 }
 
-void
+static void
 isiout_writeheader( FILE *outptr, param *p )
 {
 	if ( p->utf8bom ) utf8_writebom( outptr );
diff --git a/bibutils/isiout.h b/bibutils/isiout.h
deleted file mode 100644
--- a/bibutils/isiout.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * isiout.h
- *
- * Copyright (c) Chris Putnam 2007-2013
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef ISIOUT_H
-#define ISIOUT_H
-
-#include <stdio.h>
-#include "bibutils.h"
-
-extern void isiout_write( fields *info, FILE *fp, param *p,
-		unsigned long refnum );
-extern void isiout_writeheader( FILE *outptr, param *p );
-
-extern void isiout_initparams( param *p, const char *progname );
-
-#endif
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-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Program and source code released under the GPL version 2
  *
@@ -24,17 +24,17 @@
 	{ "C1",     "ADDRESS",   SIMPLE, LEVEL_MAIN }, /* author address */
 	{ "PA",     "ADDRESS",   SIMPLE, LEVEL_HOST }, /* publisher address */
 	{ "RP",     "REPRINTADDRESS", SIMPLE, LEVEL_MAIN },
-	{ "PY",     "PARTYEAR",  SIMPLE, LEVEL_MAIN },
-	{ "PD",     "PARTMONTH", SIMPLE, LEVEL_MAIN },
+	{ "PY",     "PARTDATE:YEAR",  SIMPLE, LEVEL_MAIN },
+	{ "PD",     "PARTDATE:MONTH", SIMPLE, LEVEL_MAIN },
 	{ "VL",     "VOLUME",    SIMPLE, LEVEL_MAIN },
 	{ "AR",     "ARTICLENUMBER", SIMPLE, LEVEL_MAIN }, /* AR=article number that Phys. Rev. B uses instead of page numbers */
-	{ "BP",     "PAGESTART", SIMPLE, LEVEL_MAIN },
-	{ "EP",     "PAGEEND",   SIMPLE, LEVEL_MAIN },
-	{ "PG",     "TOTALPAGES", SIMPLE, LEVEL_MAIN },
+	{ "BP",     "PAGES:START", SIMPLE, LEVEL_MAIN },
+	{ "EP",     "PAGES:STOP",   SIMPLE, LEVEL_MAIN },
+	{ "PG",     "PAGES:TOTAL", SIMPLE, LEVEL_MAIN },
 	{ "IS",     "ISSUE",     SIMPLE, LEVEL_MAIN },
 	{ "SN",     "SERIALNUMBER", SERIALNO, LEVEL_HOST },
 	{ "AB",     "ABSTRACT",  SIMPLE, LEVEL_MAIN },
-	{ "NF",     "NOTES",     SIMPLE, LEVEL_MAIN },
+	{ "NF",     "NOTES",     NOTES,  LEVEL_MAIN },
 	{ "DE",     "KEYWORD",   KEYWORD, LEVEL_MAIN }, /* author keywords */
 	{ "ID",     "KEYWORD",   KEYWORD, LEVEL_MAIN }, /* new ISI keywords */
 	{ "LA",     "LANGUAGE",  SIMPLE, LEVEL_MAIN },
@@ -67,16 +67,16 @@
 	{ "C1",     "ADDRESS",   SIMPLE, LEVEL_MAIN }, /* author address */
 	{ "PA",     "ADDRESS",   SIMPLE, LEVEL_MAIN }, /* publisher address */
 	{ "RP",     "REPRINTADDRESS", SIMPLE, LEVEL_MAIN },
-	{ "PY",     "YEAR",      SIMPLE, LEVEL_MAIN },
-	{ "PD",     "MONTH",     SIMPLE, LEVEL_MAIN },
+	{ "PY",     "DATE:YEAR",      SIMPLE, LEVEL_MAIN },
+	{ "PD",     "DATE:MONTH",     SIMPLE, LEVEL_MAIN },
 	{ "VL",     "VOLUME",    SIMPLE, LEVEL_MAIN },
-	{ "BP",     "PAGESTART", SIMPLE, LEVEL_MAIN },
-	{ "EP",     "PAGEEND",   SIMPLE, LEVEL_MAIN },
-	{ "PG",     "TOTALPAGES", SIMPLE, LEVEL_MAIN },
-	{ "IS",     "ISSUE",     SIMPLE, LEVEL_MAIN },
+	{ "BP",     "PAGES:START", SIMPLE, LEVEL_MAIN },
+	{ "EP",     "PAGES:STOP",  SIMPLE, LEVEL_MAIN },
+	{ "PG",     "PAGES:TOTAL", SIMPLE, LEVEL_MAIN },
+	{ "IS",     "ISSUE",       SIMPLE, LEVEL_MAIN },
 	{ "SN",     "SERIALNUMBER", SERIALNO, LEVEL_HOST },
 	{ "AB",     "ABSTRACT",  SIMPLE, LEVEL_MAIN },
-	{ "NF",     "NOTES",     SIMPLE, LEVEL_MAIN },
+	{ "NF",     "NOTES",     NOTES,  LEVEL_MAIN },
 	{ "DE",     "KEYWORD",   KEYWORD, LEVEL_MAIN }, /* author keywords */
 	{ "ID",     "KEYWORD",   KEYWORD, LEVEL_MAIN }, /* new ISI keywords */
 	{ "LA",     "LANGUAGE",  SIMPLE, LEVEL_MAIN },
@@ -108,16 +108,16 @@
 	{ "C1",     "ADDRESS",   SIMPLE, LEVEL_MAIN }, /* author address */
 	{ "PA",     "ADDRESS",   SIMPLE, LEVEL_HOST }, /* publisher address */
 	{ "RP",     "REPRINTADDRESS", SIMPLE, LEVEL_MAIN },
-	{ "PY",     "PARTYEAR",  SIMPLE, LEVEL_MAIN },
-	{ "PD",     "PARTMONTH", SIMPLE, LEVEL_MAIN },
+	{ "PY",     "PARTDATE:YEAR",  SIMPLE, LEVEL_MAIN },
+	{ "PD",     "PARTDATE:MONTH", SIMPLE, LEVEL_MAIN },
 	{ "VL",     "VOLUME",    SIMPLE, LEVEL_MAIN },
-	{ "BP",     "PAGESTART", SIMPLE, LEVEL_MAIN },
-	{ "EP",     "PAGEEND",   SIMPLE, LEVEL_MAIN },
-	{ "PG",     "TOTALPAGES", SIMPLE, LEVEL_HOST },
-	{ "IS",     "ISSUE",     SIMPLE, LEVEL_MAIN },
+	{ "BP",     "PAGES:START", SIMPLE, LEVEL_MAIN },
+	{ "EP",     "PAGES:STOP",  SIMPLE, LEVEL_MAIN },
+	{ "PG",     "PAGES:TOTAL", SIMPLE, LEVEL_HOST },
+	{ "IS",     "ISSUE",       SIMPLE, LEVEL_MAIN },
 	{ "SN",     "SERIALNUMBER", SERIALNO, LEVEL_HOST },
 	{ "AB",     "ABSTRACT",  SIMPLE, LEVEL_MAIN },
-	{ "NF",     "NOTES",     SIMPLE, LEVEL_MAIN },
+	{ "NF",     "NOTES",     NOTES,  LEVEL_MAIN },
 	{ "DE",     "KEYWORD",   KEYWORD, LEVEL_MAIN }, /* author keywords */
 	{ "ID",     "KEYWORD",   KEYWORD, LEVEL_MAIN }, /* new ISI keywords */
 	{ "LA",     "LANGUAGE",  SIMPLE, LEVEL_MAIN },
@@ -150,16 +150,16 @@
 	{ "C1",     "ADDRESS",   SIMPLE, LEVEL_MAIN }, /* author address */
 	{ "PA",     "ADDRESS",   SIMPLE, LEVEL_HOST }, /* publisher address */
 	{ "RP",     "REPRINTADDRESS", SIMPLE, LEVEL_MAIN },
-	{ "PY",     "PARTYEAR",  SIMPLE, LEVEL_MAIN },
-	{ "PD",     "PARTMONTH", SIMPLE, LEVEL_MAIN },
+	{ "PY",     "PARTDATE:YEAR",  SIMPLE, LEVEL_MAIN },
+	{ "PD",     "PARTDATE:MONTH", SIMPLE, LEVEL_MAIN },
 	{ "VL",     "VOLUME",    SIMPLE, LEVEL_MAIN },
-	{ "BP",     "PAGESTART", SIMPLE, LEVEL_MAIN },
-	{ "EP",     "PAGEEND",   SIMPLE, LEVEL_MAIN },
-	{ "PG",     "TOTALPAGES", SIMPLE, LEVEL_MAIN },
+	{ "BP",     "PAGES:START", SIMPLE, LEVEL_MAIN },
+	{ "EP",     "PAGES:STOP",  SIMPLE, LEVEL_MAIN },
+	{ "PG",     "PAGES:TOTAL", SIMPLE, LEVEL_MAIN },
 	{ "IS",     "ISSUE",     SIMPLE, LEVEL_MAIN },
 	{ "SN",     "SERIALNUMBER", SERIALNO, LEVEL_HOST },
 	{ "AB",     "ABSTRACT",  SIMPLE, LEVEL_MAIN },
-	{ "NF",     "NOTES",     SIMPLE, LEVEL_MAIN },
+	{ "NF",     "NOTES",     NOTES,  LEVEL_MAIN },
 	{ "DE",     "KEYWORD",   KEYWORD, LEVEL_MAIN }, /* author keywords */
 	{ "ID",     "KEYWORD",   KEYWORD, LEVEL_MAIN }, /* new ISI keywords */
 	{ "LA",     "LANGUAGE",  SIMPLE, LEVEL_MAIN },
diff --git a/bibutils/latex.c b/bibutils/latex.c
--- a/bibutils/latex.c
+++ b/bibutils/latex.c
@@ -3,489 +3,493 @@
  *
  * convert between latex special chars and unicode
  *
- * Copyright (c) Chris Putnam 2004-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
  */
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include "latex.h"
 
+#define LATEX_COMBO (0)  /* 'combo' no need for protection on output */
+#define LATEX_MACRO (1)  /* 'macro_name' to be protected by {\macro_name} on output */
+#define LATEX_MATH  (2)  /* 'math_expression' to be protected by $math_expression$ on output */
+
 struct latex_chars {
-	unsigned int unicode;
-	char *bib1, *bib2, *bib3;
+	unsigned int unicode;      /* unicode code point */
+	unsigned char type;        /* LATEX_COMBO/LATEX_MACRO/LATEX_MATH */
+	char *out;                 /* unadorned latex combination for output */
+	char *variant[3];         /* possible variations on input */
 };
 
 static struct latex_chars latex_chars[] = { 
 
-   {  35, "\\#",     "",   ""        },    /* Number/pound/hash sign */
-   {  36, "\\$",     "",   ""        },    /* Dollar Sign */
-   {  37, "\\%",     "",   ""        },    /* Percent Sign */
-   {  38, "\\&",     "",   ""        },    /* Ampersand */
-   {  92, "{\\backslash}", "\\backslash", ""   }, /* Backslash */
-   { 123, "\\{", "{\\textbraceleft}", "\\textbraceleft" }, /* Left Curly Bracket */
-   { 125, "\\}", "{\\textbraceright}", "\\textbraceright" }, /* Right Curly Bracket */
-   {  95, "\\_",     "", ""        },      /* Underscore alone indicates subscript */
-   { 176, "{\\textdegree}", "\\textdegree", "^\\circ" }, /* Degree sign */
-/* fix sticky spaces in bibtex token cleaning--allows tokens to be parsed properly */
-/*   {  32, "~",       "\\ ", ""        }, */   /* "sticky" space */
-   {  32, "\\ ",     "",   ""           },      /* escaping the space is used to avoid extra space after periods */
-   { 126, "{\\textasciitilde}", "\\textasciitilde", "\\~{}" }, /* Tilde in latex */
-                                 /* This is a cheat, should use "\verb" */
-                                 /* Need same for ^ character */
+                                            /* LaTeX special characters */
+   {  32, LATEX_COMBO, " ",              { "\\ ",                NULL,                 NULL               } }, /* escaping the space is used to avoid extra space after periods */
+   {  35, LATEX_MACRO, "#",              { "\\#",                NULL,                 NULL               } }, /* Number/pound/hash sign */
+   {  36, LATEX_MACRO, "$",              { "\\$",                NULL,                 NULL               } }, /* Dollar Sign */
+   {  37, LATEX_MACRO, "%",              { "\\%",                NULL,                 NULL               } }, /* Percent Sign */
+   {  38, LATEX_MACRO, "&",              { "\\&",                NULL,                 NULL               } }, /* Ampersand */
+   {  95, LATEX_MACRO, "_",              { "\\_",                NULL,                 NULL               } }, /* Underscore alone indicates subscript */
+   { 123, LATEX_MACRO, "{",              { "\\{",                "{\\textbraceleft}",  "\\textbraceleft"  } }, /* Left Curly Bracket */
+   { 125, LATEX_MACRO, "}",              { "\\}",                "{\\textbraceright}", "\\textbraceright" } }, /* Right Curly Bracket */
+   {  92, LATEX_MACRO, "backslash",      { "{\\backslash}",      "\\backslash",        NULL               } }, /* Backslash */
+   { 176, LATEX_MACRO, "textdegree",     { "{\\textdegree}",     "\\textdegree",       "^\\circ"          } }, /* Degree sign */
+   { 126, LATEX_MACRO, "textasciitilde", { "{\\textasciitilde}", "\\textasciitilde",   "\\~{}"            } }, /* Tilde in latex */
+/* fix sticky spaces "~" in bibtex token cleaning--allows tokens to be parsed properly */
 
-                                           /* Latin Capital A */
-   { 192, "{\\`A}",  "\\`{A}",  "\\`A"  }, /*               with grave */
-   { 193, "{\\'A}",  "\\'{A}",  "\\'A"  }, /*               with acute */
-   { 194, "{\\^A}",  "\\^{A}",  "\\^A"  }, /*               with circumflex */
-   { 195, "{\\~A}",  "\\~{A}",  "\\~A"  }, /*               with tilde */
-   { 196, "{\\\"A}", "\\\"{A}", "\\\"A" }, /*               with diuresis */
-   { 197, "{\\AA}",  "{\\r{A}}","\\r{A}"}, /*               with ring above */
-   { 256, "{\\={A}}","\\={A}",  "\\=A"  }, /*               with macron */
-   { 258, "{\\u{A}}","\\u{A}",  "\\u A" }, /*               with breve */
-   { 260, "{\\k{A}}","\\k{A}",  "\\k A" }, /*               with ogonek */
-   { 461, "{\\v{A}}","\\v{A}",  "\\v A" }, /*               with caron */
+                                            /* Latin Capital A */
+   { 192, LATEX_MACRO, "`A",   { "{\\`A}",   "\\`{A}",   "\\`A"  } }, /*               with grave */
+   { 193, LATEX_MACRO, "'A",   { "{\\'A}",   "\\'{A}",   "\\'A"  } }, /*               with acute */
+   { 194, LATEX_MACRO, "^A",   { "{\\^A}",   "\\^{A}",   "\\^A"  } }, /*               with circumflex */
+   { 195, LATEX_MACRO, "~A",   { "{\\~A}",   "\\~{A}",   "\\~A"  } }, /*               with tilde */
+   { 196, LATEX_MACRO, "\"A",  { "{\\\"A}",  "\\\"{A}",  "\\\"A" } }, /*               with diuresis */
+   { 197, LATEX_MACRO, "AA",   { "{\\AA}",   "{\\r{A}}", "\\r{A}"} }, /*               with ring above */
+   { 256, LATEX_MACRO, "={A}", { "{\\={A}}", "\\={A}",   "\\=A"  } }, /*               with macron */
+   { 258, LATEX_MACRO, "u{A}", { "{\\u{A}}", "\\u{A}",   "\\u A" } }, /*               with breve */
+   { 260, LATEX_MACRO, "k{A}", { "{\\k{A}}", "\\k{A}",   "\\k A" } }, /*               with ogonek */
+   { 461, LATEX_MACRO, "v{A}", { "{\\v{A}}", "\\v{A}",   "\\v A" } }, /*               with caron */
 
                                            /* Latin Small a */
-   { 224, "{\\`a}",  "\\`{a}",  "\\`a"  }, /*               with grave */
-   { 225, "{\\'a}",  "\\'{a}",  "\\'a"  }, /*               with acute */
-   { 226, "{\\^a}",  "\\^{a}",  "\\^a"  }, /*               with circumflex */
-   { 227, "{\\~a}",  "\\~{a}",  "\\~a"  }, /*               with tilde */
-   { 228, "{\\\"a}", "\\\"{a}", "\\\"a" }, /*               with diuresis */
-   { 229, "{\\aa}",  "{\\r{a}}","\\r{a}"}, /*               with ring above */
-   { 257, "{\\={a}}","\\={a}",  "\\=a"  }, /*               with macron */
-   { 259, "{\\u{a}}","\\u{a}",  "\\u a" }, /*               with breve */
-   { 261, "{\\k{a}}","\\k{a}",  "\\k a" }, /*               with ogonek */
-   { 462, "{\\v{a}}","\\v{a}",  "\\v a" }, /*               with caron */
+   { 224, LATEX_MACRO, "`a",   { "{\\`a}",   "\\`{a}",   "\\`a"  } }, /*               with grave */
+   { 225, LATEX_MACRO, "'a",   { "{\\'a}",   "\\'{a}",   "\\'a"  } }, /*               with acute */
+   { 226, LATEX_MACRO, "^a",   { "{\\^a}",   "\\^{a}",   "\\^a"  } }, /*               with circumflex */
+   { 227, LATEX_MACRO, "~a",   { "{\\~a}",   "\\~{a}",   "\\~a"  } }, /*               with tilde */
+   { 228, LATEX_MACRO, "\"a",  { "{\\\"a}",  "\\\"{a}",  "\\\"a" } }, /*               with diuresis */
+   { 229, LATEX_MACRO, "aa",   { "{\\aa}",   "{\\r{a}}", "\\r{a}"} }, /*               with ring above */
+   { 257, LATEX_MACRO, "={a}", { "{\\={a}}", "\\={a}",   "\\=a"  } }, /*               with macron */
+   { 259, LATEX_MACRO, "u{a}", { "{\\u{a}}", "\\u{a}",   "\\u a" } }, /*               with breve */
+   { 261, LATEX_MACRO, "k{a}", { "{\\k{a}}", "\\k{a}",   "\\k a" } }, /*               with ogonek */
+   { 462, LATEX_MACRO, "v{a}", { "{\\v{a}}", "\\v{a}",   "\\v a" } }, /*               with caron */
 
-   { 198, "{\\AE}",  "\\AE",    ""      }, /* Latin Capital AE */
-   { 230, "{\\ae}",  "\\ae",    ""      }, /* Latin Small ae */
+   { 198, LATEX_MACRO, "AE",   { "{\\AE}",   "\\AE",     NULL    } }, /* Latin Capital AE */
+   { 230, LATEX_MACRO, "ae",   { "{\\ae}",   "\\ae",     NULL    } }, /* Latin Small ae */
 
                                            /* Latin Capital C */
-   { 199, "{\\c{C}}","\\c{C}",  "\\c c" }, /*               with cedilla */
-   { 262, "{\\'{C}}","\\'{C}",  "\\'C"  }, /*               with acute */
-   { 264, "{\\^{C}}","\\^{C}",  "\\^C"  }, /*               with circumflex */
-   { 266, "{\\.{C}}","\\.{C}",  "\\.C"  }, /*               with dot above */
-   { 268, "{\\v{C}}","\\v{C}",  "\\v C" }, /*               with caron (hacek) */
+   { 199, LATEX_MACRO, "c{C}", { "{\\c{C}}", "\\c{C}",  "\\c c"  } }, /*               with cedilla */
+   { 262, LATEX_MACRO, "'{C}", { "{\\'{C}}", "\\'{C}",  "\\'C"   } }, /*               with acute */
+   { 264, LATEX_MACRO, "^{C}", { "{\\^{C}}", "\\^{C}",  "\\^C"   } }, /*               with circumflex */
+   { 266, LATEX_MACRO, ".{C}", { "{\\.{C}}", "\\.{C}",  "\\.C"   } }, /*               with dot above */
+   { 268, LATEX_MACRO, "v{C}", { "{\\v{C}}", "\\v{C}",  "\\v C"  } }, /*               with caron (hacek) */
 
                                            /* Latin Small c */
-   { 231, "{\\c{c}}","\\c{c}",  "\\c C" }, /*               with cedilla*/
-   { 263, "{\\'{c}}","\\'{c}",  "\\'c"  }, /*               with acute */
-   { 265, "{\\^{c}}","\\^{c}",  "\\^c"  }, /*               with circumflex */
-   { 267, "{\\.{c}}","\\.{c}",  "\\.c"  }, /*               with dot above */
-   { 269, "{\\v{c}}","\\v{c}",  "\\v c" }, /*               with caron (hacek) */
+   { 231, LATEX_MACRO, "c{c}", { "{\\c{c}}", "\\c{c}",  "\\c C"  } }, /*               with cedilla*/
+   { 263, LATEX_MACRO, "'{c}", { "{\\'{c}}", "\\'{c}",  "\\'c"   } }, /*               with acute */
+   { 265, LATEX_MACRO, "^{c}", { "{\\^{c}}", "\\^{c}",  "\\^c"   } }, /*               with circumflex */
+   { 267, LATEX_MACRO, ".{c}", { "{\\.{c}}", "\\.{c}",  "\\.c"   } }, /*               with dot above */
+   { 269, LATEX_MACRO, "v{c}", { "{\\v{c}}", "\\v{c}",  "\\v c"  } }, /*               with caron (hacek) */
 
                                            /* Latin Capital D */
-   { 270, "{\\v{D}}","\\v{D}",  "\\v D" }, /*               with caron */
-   { 272, "{\\DJ}",  "",        ""      }, /*               with stroke */
+   { 270, LATEX_MACRO, "v{D}", { "{\\v{D}}", "\\v{D}",  "\\v D"  } }, /*               with caron */
+   { 272, LATEX_MACRO, "DJ",   { "{\\DJ}",   NULL,      NULL     } }, /*               with stroke */
 
                                            /* Latin Small d */
-   { 271, "{\\v{d}}","\\v{d}",  "\\v d" }, /*               with caron */
-   { 273, "{\\dj}",  "",        ""      }, /*               with stroke */
+   { 271, LATEX_MACRO, "v{d}", { "{\\v{d}}", "\\v{d}",  "\\v d"  } }, /*               with caron */
+   { 273, LATEX_MACRO, "dj",   { "{\\dj}",   NULL,      NULL     } }, /*               with stroke */
 
                                            /* Latin Capital E */
-   { 200, "{\\`E}",  "\\`{E}",  "\\`E"  }, /*               with grave */
-   { 201, "{\\'E}",  "\\'{E}",  "\\'E"  }, /*               with acute */
-   { 202, "{\\^E}",  "\\^{E}",  "\\^E"  }, /*               with circumflex */
-   { 203, "{\\\"E}", "\\\"{E}", "\\\"E" }, /*               with diuresis */
-   { 274, "{\\={E}}","\\={E}",  "\\=E"  }, /*               with macron */
-   { 276, "{\\u{E}}","\\u{E}",  "\\u E" }, /*               with breve */
-   { 278, "{\\.{E}}","\\.{E}",  "\\.E"  }, /*               with dot above */
-   { 280, "{\\k{E}}","\\k{E}",  "\\k E" }, /*               with ogonek */
-   { 282, "{\\v{E}}","\\v{E}",  "\\v E" }, /*               with caron */
+   { 200, LATEX_MACRO, "`E",   { "{\\`E}",   "\\`{E}",  "\\`E"   } }, /*               with grave */
+   { 201, LATEX_MACRO, "'E",   { "{\\'E}",   "\\'{E}",  "\\'E"   } }, /*               with acute */
+   { 202, LATEX_MACRO, "^E",   { "{\\^E}",   "\\^{E}",  "\\^E"   } }, /*               with circumflex */
+   { 203, LATEX_MACRO, "\"E",  { "{\\\"E}",  "\\\"{E}", "\\\"E"  } }, /*               with diuresis */
+   { 274, LATEX_MACRO, "={E}", { "{\\={E}}", "\\={E}",  "\\=E"   } }, /*               with macron */
+   { 276, LATEX_MACRO, "u{E}", { "{\\u{E}}", "\\u{E}",  "\\u E"  } }, /*               with breve */
+   { 278, LATEX_MACRO, ".{E}", { "{\\.{E}}", "\\.{E}",  "\\.E"   } }, /*               with dot above */
+   { 280, LATEX_MACRO, "k{E}", { "{\\k{E}}", "\\k{E}",  "\\k E"  } }, /*               with ogonek */
+   { 282, LATEX_MACRO, "v{E}", { "{\\v{E}}", "\\v{E}",  "\\v E"  } }, /*               with caron */
  
                                            /* Latin Small e */
-   { 232, "{\\`e}",  "\\`{e}",  "\\`e"  }, /*               with grave */
-   { 233, "{\\'e}",  "\\'{e}",  "\\'e"  }, /*               with acute */
-   { 234, "{\\^e}",  "\\^{e}",  "\\^e"  }, /*               with circumflex */
-   { 235, "{\\\"e}", "\\\"{e}", "\\\"e" }, /*               with diuresis */
-   { 275, "{\\={e}}","\\={e}",  "\\=e"  }, /*               with macron */
-   { 277, "{\\u{e}}","\\u{e}",  "\\u e" }, /*               with breve */
-   { 279, "{\\.{e}}","\\.{e}",  "\\.e"  }, /*               with dot above */
-   { 281, "{\\k{e}}","\\k{e}",  "\\k e" }, /*               with ogonek */
-   { 283, "{\\v{e}}","\\v{e}",  "\\v e" }, /*               with caron */
+   { 232, LATEX_MACRO, "`e",   { "{\\`e}",   "\\`{e}",  "\\`e"   } }, /*               with grave */
+   { 233, LATEX_MACRO, "'e",   { "{\\'e}",   "\\'{e}",  "\\'e"   } }, /*               with acute */
+   { 234, LATEX_MACRO, "^e",   { "{\\^e}",   "\\^{e}",  "\\^e"   } }, /*               with circumflex */
+   { 235, LATEX_MACRO, "\"e",  { "{\\\"e}",  "\\\"{e}", "\\\"e"  } }, /*               with diuresis */
+   { 275, LATEX_MACRO, "={e}", { "{\\={e}}", "\\={e}",  "\\=e"   } }, /*               with macron */
+   { 277, LATEX_MACRO, "u{e}", { "{\\u{e}}", "\\u{e}",  "\\u e"  } }, /*               with breve */
+   { 279, LATEX_MACRO, ".{e}", { "{\\.{e}}", "\\.{e}",  "\\.e"   } }, /*               with dot above */
+   { 281, LATEX_MACRO, "k{e}", { "{\\k{e}}", "\\k{e}",  "\\k e"  } }, /*               with ogonek */
+   { 283, LATEX_MACRO, "v{e}", { "{\\v{e}}", "\\v{e}",  "\\v e"  } }, /*               with caron */
 
                                            /* Latin Capital G */
-   { 284, "{\\^{G}}","\\^{G}",  "\\^G"  }, /*               with circumflex */
-   { 286, "{\\u{G}}","\\u{G}",  "\\u G" }, /*               with breve */
-   { 288, "{\\.{G}}","\\.{G}",  "\\.G"  }, /*               with dot above */
-   { 290, "{\\c{G}}","\\c{G}",  "\\c G" }, /*               with cedilla */
-   { 486, "{\\v{G}}","\\v{G}",  "\\v G" }, /*               with caron */
-   { 500, "{\\'{G}}","\\'{G}",  "\\'G"  }, /*               with acute */
+   { 284, LATEX_MACRO, "^{G}", { "{\\^{G}}", "\\^{G}",  "\\^G"   } }, /*               with circumflex */
+   { 286, LATEX_MACRO, "u{G}", { "{\\u{G}}", "\\u{G}",  "\\u G"  } }, /*               with breve */
+   { 288, LATEX_MACRO, ".{G}", { "{\\.{G}}", "\\.{G}",  "\\.G"   } }, /*               with dot above */
+   { 290, LATEX_MACRO, "c{G}", { "{\\c{G}}", "\\c{G}",  "\\c G"  } }, /*               with cedilla */
+   { 486, LATEX_MACRO, "v{G}", { "{\\v{G}}", "\\v{G}",  "\\v G"  } }, /*               with caron */
+   { 500, LATEX_MACRO, "'{G}", { "{\\'{G}}", "\\'{G}",  "\\'G"   } }, /*               with acute */
 
                                            /* Latin Small g */
-   { 285, "{\\^{g}}","\\^{g}",  "\\^g"  }, /*               with circumflex */
-   { 287, "{\\u{g}}","\\u{g}",  "\\u g" }, /*               with breve */
-   { 289, "{\\.{g}}","\\.{g}",  "\\.g"  }, /*               with dot above */
-   { 291, "{\\c{g}}","\\c{g}",  "\\c g" }, /*               with cedilla */
-   { 487, "{\\v{g}}","\\v{g}",  "\\v g" }, /*               with caron */
-   { 501, "{\\'{g}}","\\'{g}",  "\\'g"  }, /*               with acute */
+   { 285, LATEX_MACRO, "^{g}", { "{\\^{g}}", "\\^{g}",  "\\^g"   } }, /*               with circumflex */
+   { 287, LATEX_MACRO, "u{g}", { "{\\u{g}}", "\\u{g}",  "\\u g"  } }, /*               with breve */
+   { 289, LATEX_MACRO, ".{g}", { "{\\.{g}}", "\\.{g}",  "\\.g"   } }, /*               with dot above */
+   { 291, LATEX_MACRO, "c{g}", { "{\\c{g}}", "\\c{g}",  "\\c g"  } }, /*               with cedilla */
+   { 487, LATEX_MACRO, "v{g}", { "{\\v{g}}", "\\v{g}",  "\\v g"  } }, /*               with caron */
+   { 501, LATEX_MACRO, "'{g}", { "{\\'{g}}", "\\'{g}",  "\\'g"   } }, /*               with acute */
 
                                            /* Latin Capital H */
-   { 292, "{\\^{H}}","\\^{H}",  "\\^H"  }, /*               with circumflex */
-/* { 294, "",        "",        ""      },*//*              with stroke */
+   { 292, LATEX_MACRO, "^{H}", { "{\\^{H}}", "\\^{H}",  "\\^H"   } }, /*               with circumflex */
+/* { 294, LATEX_MACRO, "",     { NULL,       NULL,      NULL     } },*//*              with stroke */
 
                                            /* Latin Capital h */
-   { 293, "{\\^{h}}","\\^{h}",  "\\^h"  }, /*               with circumflex */
-/* { 295, "",        "",        ""      },*//*              with stroke */
+   { 293, LATEX_MACRO, "^{h}", { "{\\^{h}}", "\\^{h}",  "\\^h"   } }, /*               with circumflex */
+/* { 295, LATEX_MACRO, "",     { NULL,       NULL,      NULL     } },*//*              with stroke */
  
                                            /* Latin Capital I */
-   { 204, "{\\`I}",  "\\`{I}",  "\\`I"  }, /*               with grave */
-   { 205, "{\\'I}",  "\\'{I}",  "\\'I"  }, /*               with acute */
-   { 206, "{\\^I}",  "\\^{I}",  "\\^I"  }, /*               with circumflex */
-   { 207, "{\\\"I}", "\\\"{I}", "\\\"I" }, /*               with diuresis */
-   { 296, "{\\~{I}}","\\~{I}",  "\\~I"  }, /*               with tilde */
-   { 298, "{\\={I}}","\\={I}",  "\\=I"  }, /*               with macron */
-   { 300, "{\\u{I}}","\\u{I}",  "\\u I" }, /*               with breve */
-   { 302, "{\\k{I}}","\\k{I}",  "\\k I" }, /*               with ogonek */
-   { 304, "{\\.{I}}","\\.{I}",  "\\. I" }, /*               with dot above */
-   { 463, "{\\v{I}}","\\v{I}",  "\\v I" }, /*               with caron */
+   { 204, LATEX_MACRO, "`I",   { "{\\`I}",   "\\`{I}",  "\\`I"   } }, /*               with grave */
+   { 205, LATEX_MACRO, "'I",   { "{\\'I}",   "\\'{I}",  "\\'I"   } }, /*               with acute */
+   { 206, LATEX_MACRO, "^I",   { "{\\^I}",   "\\^{I}",  "\\^I"   } }, /*               with circumflex */
+   { 207, LATEX_MACRO, "\"I",  { "{\\\"I}",  "\\\"{I}", "\\\"I"  } }, /*               with diuresis */
+   { 296, LATEX_MACRO, "~{I}", { "{\\~{I}}", "\\~{I}",  "\\~I"   } }, /*               with tilde */
+   { 298, LATEX_MACRO, "={I}", { "{\\={I}}", "\\={I}",  "\\=I"   } }, /*               with macron */
+   { 300, LATEX_MACRO, "u{I}", { "{\\u{I}}", "\\u{I}",  "\\u I"  } }, /*               with breve */
+   { 302, LATEX_MACRO, "k{I}", { "{\\k{I}}", "\\k{I}",  "\\k I"  } }, /*               with ogonek */
+   { 304, LATEX_MACRO, ".{I}", { "{\\.{I}}", "\\.{I}",  "\\. I"  } }, /*               with dot above */
+   { 463, LATEX_MACRO, "v{I}", { "{\\v{I}}", "\\v{I}",  "\\v I"  } }, /*               with caron */
 
                                            /* Latin Small i */
-   { 236, "{\\`i}",  "\\`{i}",  "\\`i"  }, /*               with grave */
-   { 237, "{\\'i}",  "\\'{i}",  "\\'i"  }, /*               with acute */
-   { 238, "{\\^i}",  "\\^{i}",  "\\^i"  }, /*               with circumflex */
-   { 239, "{\\\"i}", "\\\"{i}", "\\\"i" }, /*               with diuresis */
-   { 236, "{\\`\\i}",  "\\`{\\i}",  "\\`\\i"  }, /*         with grave */
-   { 237, "{\\'\\i}",  "\\'{\\i}",  "\\'\\i"  }, /*         with acute */
-   { 238, "{\\^\\i}",  "\\^{\\i}",  "\\^\\i"  }, /*         with circumflex */
-   { 239, "{\\\"\\i}", "\\\"{\\i}", "\\\"\\i" }, /*         with diuresis */
-   { 297, "{\\~{\\i}}","\\~{\\i}",  "\\~\\i{}"}, /*         with tilde */
-   { 299, "{\\={\\i}}", "\\={\\i}", "\\=\\i{}"}, /*         with macron */
-   { 301, "{\\u{\\i}}","\\u{\\i}",  "\\u\\i{}"}, /*         with breve */
-   { 303, "{\\k{i}}","\\k{i}",  "\\k i" }, /*               with ogonek */
-                                           /* Latex \i has no dot on "i"*/
-   { 305, "{\\i}",     "\\i{}",     ""        }, /*         without dot above */
-   { 464, "{\\v \\i{}}", "\\v \\i{}", ""      }, /*         with caron */
+   { 236, LATEX_MACRO, "`i",   { "{\\`i}",   "\\`{i}",  "\\`i"   } }, /*               with grave */
+   { 237, LATEX_MACRO, "'i",   { "{\\'i}",   "\\'{i}",  "\\'i"   } }, /*               with acute */
+   { 238, LATEX_MACRO, "^i",   { "{\\^i}",   "\\^{i}",  "\\^i"   } }, /*               with circumflex */
+   { 239, LATEX_MACRO, "\"i",  { "{\\\"i}",  "\\\"{i}", "\\\"i"  } }, /*               with diuresis */
+   { 303, LATEX_MACRO, "k{i}", { "{\\k{i}}", "\\k{i}",  "\\k i"  } }, /*               with ogonek */
+                                           /* Latex "\i" has no dot on "i" */
+   { 305, LATEX_MACRO, "i",       { "{\\i}",      "\\i{}",    NULL       } }, /*    without dot above */
+   { 236, LATEX_MACRO, "`{\\i}",  { "{\\`\\i}",   "\\`{\\i}", "\\`\\i"   } }, /*       with grave */
+   { 237, LATEX_MACRO, "'{\\i}",  { "{\\'\\i}",   "\\'{\\i}", "\\'\\i"   } }, /*       with acute */
+   { 238, LATEX_MACRO, "^{\\i}",  { "{\\^\\i}",   "\\^{\\i}", "\\^\\i"   } }, /*       with circumflex */
+   { 239, LATEX_MACRO, "\"{\\i}", { "{\\\"\\i}",  "\\\"{\\i}","\\\"\\i"  } }, /*       with diuresis */
+   { 297, LATEX_MACRO, "`{\\i}",  { "{\\~{\\i}}", "\\~{\\i}", "\\~\\i{}" } }, /*       with tilde */
+   { 299, LATEX_MACRO, "={\\i}",  { "{\\={\\i}}", "\\={\\i}", "\\=\\i{}" } }, /*       with macron */
+   { 301, LATEX_MACRO, "u{\\i}",  { "{\\u{\\i}}", "\\u{\\i}", "\\u\\i{}" } }, /*       with breve */
+   { 464, LATEX_MACRO, "v{\\i}",  { "{\\v \\i{}}", "\\v \\i{}", NULL   } }, /*         with caron */
 
-/*   { 306, "",  "",    ""      },*/ /* Latin Capital IJ */
-/*   { 307, ""  "",    ""       },*/ /* Latin Small ij */
+/* { 306, LATEX_MACRO, "",     { NULL,       NULL,      NULL     } },*/ /* Latin Capital IJ */
+/* { 307, LATEX_MACRO, "",     { NULL,       NULL,      NULL     } },*/ /* Latin Small ij */
 
                                            /* Latin Capital J */
-   { 308, "{\\^{J}}","\\^{J}",  "\\^J"  }, /*                with circumflex */
+   { 308, LATEX_MACRO, "^{J}", { "{\\^{J}}", "\\^{J}",  "\\^J"   } }, /*               with circumflex */
 
                                            /* Latin Small j */
-   { 309, "{\\^{j}}","\\^{j}",  "\\^j"  }, /*                with circumflex */
+   { 309, LATEX_MACRO, "^{j}", { "{\\^{j}}", "\\^{j}",  "\\^j"   } }, /*               with circumflex */
 
                                            /* Latin Capital K */
-   { 310, "{\\c{K}}","\\c{K}",  "\\c K" }, /*               with cedilla */
-   { 488, "{\\v{K}}","\\v{K}",  "\\v K" }, /*               with caron */
+   { 310, LATEX_MACRO, "c{K}", { "{\\c{K}}", "\\c{K}",  "\\c K"  } }, /*               with cedilla */
+   { 488, LATEX_MACRO, "v{K}", { "{\\v{K}}", "\\v{K}",  "\\v K"  } }, /*               with caron */
 
                                            /* Latin Small k */
-   { 311, "{\\c{k}}","\\c{k}",  "\\c k" }, /*               with cedilla */
-   { 489, "{\\v{k}}","\\v{k}",  "\\v k" }, /*               with caron */
+   { 311, LATEX_MACRO, "c{k}", { "{\\c{k}}", "\\c{k}",  "\\c k"  } }, /*               with cedilla */
+   { 489, LATEX_MACRO, "v{k}", { "{\\v{k}}", "\\v{k}",  "\\v k"  } }, /*               with caron */
 
                                            /* Latin Capital L */
-   { 313, "{\\'{L}}","\\'{L}",  "\\'L"  }, /*               with acute */
-   { 315, "{\\c{L}}","\\c{L}",  "\\c L" }, /*               with cedilla */
-   { 317, "{\\v{L}}","\\v{l}",  "\\v L" }, /*               with caron */
-   { 319, "{L\\hspace{-0.35em}$\\cdot$}","L\\hspace{-0.35em}$\\cdot$", "" }, /*               with middle dot */
-   { 321, "{\\L}",   "{\\L{}}", "\\L{}" }, /*               with stroke */
+   { 313, LATEX_MACRO, "'{L}", { "{\\'{L}}", "\\'{L}",  "\\'L"   } }, /*               with acute */
+   { 315, LATEX_MACRO, "c{L}", { "{\\c{L}}", "\\c{L}",  "\\c L"  } }, /*               with cedilla */
+   { 317, LATEX_MACRO, "v{L}", { "{\\v{L}}", "\\v{l}",  "\\v L"  } }, /*               with caron */
+   { 319, LATEX_COMBO, "{L\\hspace{-0.35em}$\\cdot$}", { "{L\\hspace{-0.35em}$\\cdot$}","L\\hspace{-0.35em}$\\cdot$", NULL } }, /*               with middle dot */
+   { 321, LATEX_MACRO, "L",    { "{\\L}",   "{\\L{}}", "\\L{}"   } }, /*               with stroke */
 
                                            /* Latin Small l */
-   { 314, "{\\'{l}}","\\'{l}",  "\\'l"  }, /*               with acute */
-   { 316, "{\\c{l}}","\\c{l}",  "\\c l" }, /*               with cedilla */
-   { 318, "{\\v{l}}","\\v{l}",  "\\v l" }, /*               with caron */
-   { 320, "{l$\\cdot$}","l$\\cdot$", "" }, /*               with middle dot */
-   { 322, "{\\l}",   "{\\l{}}", "\\l{}" }, /*               with stroke */
+   { 314, LATEX_MACRO, "'{l}", { "{\\'{l}}", "\\'{l}",  "\\'l"   } }, /*               with acute */
+   { 316, LATEX_MACRO, "c{l}", { "{\\c{l}}", "\\c{l}",  "\\c l"  } }, /*               with cedilla */
+   { 318, LATEX_MACRO, "v{l}", { "{\\v{l}}", "\\v{l}",  "\\v l"  } }, /*               with caron */
+   { 320, LATEX_COMBO, "{l$\\cdot$}", { "{l$\\cdot$}","l$\\cdot$", NULL } }, /*               with middle dot */
+   { 322, LATEX_MACRO, "l",    { "{\\l}",    "{\\l{}}", "\\l{}"  } }, /*               with stroke */
 
                                            /* Latin Capital N */
-   { 209, "{\\~{N}}","\\~{N}",  "\\~N"  }, /*               with tilde */
-   { 323, "{\\'{N}}","\\'{N}",  "\\'N"  }, /*               with acute */
-   { 325, "{\\c{N}}","\\c{N}",  "\\c N" }, /*               with cedilla */
-   { 327, "{\\v{N}}","\\v{N}",  "\\v N" }, /*               with caron */
+   { 209, LATEX_MACRO, "~{N}", { "{\\~{N}}", "\\~{N}",  "\\~N"   } }, /*               with tilde */
+   { 323, LATEX_MACRO, "'{N}", { "{\\'{N}}", "\\'{N}",  "\\'N"   } }, /*               with acute */
+   { 325, LATEX_MACRO, "c{N}", { "{\\c{N}}", "\\c{N}",  "\\c N"  } }, /*               with cedilla */
+   { 327, LATEX_MACRO, "v{N}", { "{\\v{N}}", "\\v{N}",  "\\v N"  } }, /*               with caron */
 
                                            /* Latin Small n */
-   { 241, "{\\~{n}}","\\~{n}",  "\\~n"  }, /*               with tilde */
-   { 324, "{\\'{n}}","\\'{n}",  "\\'n"  }, /*               with acute */
-   { 326, "{\\c{n}}","\\c{n}",  "\\c N" }, /*               with cedilla */
-   { 328, "{\\v{n}}","\\v{n}",  "\\v n" }, /*               with caron */
-   { 329, "\n",      "",        ""      }, /*               preceeded by apostrophe */
+   { 241, LATEX_MACRO, "~{n}", { "{\\~{n}}", "\\~{n}",  "\\~n"   } }, /*               with tilde */
+   { 324, LATEX_MACRO, "'{n}", { "{\\'{n}}", "\\'{n}",  "\\'n"   } }, /*               with acute */
+   { 326, LATEX_MACRO, "c{n}", { "{\\c{n}}", "\\c{n}",  "\\c N"  } }, /*               with cedilla */
+   { 328, LATEX_MACRO, "v{n}", { "{\\v{n}}", "\\v{n}",  "\\v n"  } }, /*               with caron */
+   { 329, LATEX_MACRO, "n",    { "\\n",      NULL,      NULL     } }, /*               preceeded by apostrophe */
  
                                            /* Latin Capital O */
-   { 210, "{\\`O}",  "\\`{O}",  "\\`O"  }, /*               with grave */
-   { 211, "{\\'O}",  "\\'{O}",  "\\'O"  }, /*               with acute */
-   { 212, "{\\^O}",  "\\^{O}",  "\\^O"  }, /*               with circumflex */
-   { 213, "{\\~O}",  "\\~{O}",  "\\~O"  }, /*               with tilde */
-   { 214, "{\\\"O}", "\\\"{O}", "\\\"O" }, /*               with diaeresis */
-   { 216, "{\\O}",   "\\O",     ""      }, /*               with stroke */
-   { 332, "{\\={O}}", "\\={O}", "\\=O"  }, /*               with macron */
-   { 334, "{\\u{O}}", "\\u{O}", "\\u O" }, /*               with breve */
-   { 336, "{\\H{O}}", "\\H{O}", "\\H O" }, /*               with double acute */
-   { 465, "{\\v{O}}", "\\v{O}", "\\v O" }, /*               with caron */
-   { 490, "{\\k{O}}", "\\k{O}", "\\k O" }, /*               with ogonek */
+   { 210, LATEX_MACRO, "`O",   { "{\\`O}",   "\\`{O}",  "\\`O"   } }, /*               with grave */
+   { 211, LATEX_MACRO, "'O",   { "{\\'O}",   "\\'{O}",  "\\'O"   } }, /*               with acute */
+   { 212, LATEX_MACRO, "^O",   { "{\\^O}",   "\\^{O}",  "\\^O"   } }, /*               with circumflex */
+   { 213, LATEX_MACRO, "~O",   { "{\\~O}",   "\\~{O}",  "\\~O"   } }, /*               with tilde */
+   { 214, LATEX_MACRO, "\"O",  { "{\\\"O}",  "\\\"{O}", "\\\"O"  } }, /*               with diaeresis */
+   { 216, LATEX_MACRO, "O",    { "{\\O}",    "\\O",     NULL     } }, /*               with stroke */
+   { 332, LATEX_MACRO, "={O}", { "{\\={O}}", "\\={O}", "\\=O"    } }, /*               with macron */
+   { 334, LATEX_MACRO, "u{O}", { "{\\u{O}}", "\\u{O}", "\\u O"   } }, /*               with breve */
+   { 336, LATEX_MACRO, "H{O}", { "{\\H{O}}", "\\H{O}", "\\H O"   } }, /*               with double acute */
+   { 465, LATEX_MACRO, "v{O}", { "{\\v{O}}", "\\v{O}", "\\v O"   } }, /*               with caron */
+   { 490, LATEX_MACRO, "k{O}", { "{\\k{O}}", "\\k{O}", "\\k O"   } }, /*               with ogonek */
 
                                            /* Latin Small o */
-   { 242, "{\\`o}",  "\\`{o}",  "\\`o"  }, /*               with grave */
-   { 243, "{\\'o}",  "\\'{o}",  "\\'o"  }, /*               with acute */
-   { 244, "{\\^o}",  "\\^{o}",  "\\^o"  }, /*               with circumflex */
-   { 245, "{\\~o}",  "\\~{o}",  "\\~o"  }, /*               with tilde */
-   { 246, "{\\\"o}", "\\\"{o}", "\\\"o" }, /*               with diaeresis */
-   { 248, "{\\o}",   "\\o",     ""      }, /*               with stroke */
-   { 333, "{\\={o}}", "\\={o}", "\\=o"  }, /*               with macron */
-   { 335, "{\\u{o}}", "\\u{o}", "\\u o" }, /*               with breve */
-   { 337, "{\\H{o}}", "\\H{o}", "\\H o" }, /*               with double acute */
-   { 466, "{\\v{o}}", "\\v{o}", "\\v o" }, /*               with caron */
-   { 491, "{\\k{o}}", "\\k{o}", "\\k o" }, /*               with ogonek */
+   { 242, LATEX_MACRO, "`o",   { "{\\`o}",   "\\`{o}",  "\\`o"   } }, /*               with grave */
+   { 243, LATEX_MACRO, "'o",   { "{\\'o}",   "\\'{o}",  "\\'o"   } }, /*               with acute */
+   { 244, LATEX_MACRO, "^o",   { "{\\^o}",   "\\^{o}",  "\\^o"   } }, /*               with circumflex */
+   { 245, LATEX_MACRO, "~o",   { "{\\~o}",   "\\~{o}",  "\\~o"   } }, /*               with tilde */
+   { 246, LATEX_MACRO, "\"o",  { "{\\\"o}",  "\\\"{o}", "\\\"o"  } }, /*               with diaeresis */
+   { 248, LATEX_MACRO, "o",    { "{\\o}",    "\\o",     NULL     } }, /*               with stroke */
+   { 333, LATEX_MACRO, "={o}", { "{\\={o}}", "\\={o}", "\\=o"    } }, /*               with macron */
+   { 335, LATEX_MACRO, "u{o}", { "{\\u{o}}", "\\u{o}", "\\u o"   } }, /*               with breve */
+   { 337, LATEX_MACRO, "H{o}", { "{\\H{o}}", "\\H{o}", "\\H o"   } }, /*               with double acute */
+   { 466, LATEX_MACRO, "v{o}", { "{\\v{o}}", "\\v{o}", "\\v o"   } }, /*               with caron */
+   { 491, LATEX_MACRO, "k{o}", { "{\\k{o}}", "\\k{o}", "\\k o"   } }, /*               with ogonek */
 
-   { 338, "{\\OE}",  "\\OE",    ""      }, /* Latin Capital OE */
-   { 339, "{\\oe}",  "\\oe",    ""      }, /* Latin Small oe */
+   { 338, LATEX_MACRO, "OE",   { "{\\OE}",   "\\OE",   NULL      } }, /* Latin Capital OE */
+   { 339, LATEX_MACRO, "oe",   { "{\\oe}",   "\\oe",   NULL      } }, /* Latin Small oe */
 
                                            /* Latin Capital R */
-   { 340, "{\\'{R}}", "\\'{R}", "\\'R"  }, /*                 with acute */
-   { 342, "{\\c{R}}", "\\c{R}", "\\c R" }, /*                 with cedilla */
-   { 344, "{\\v{R}}", "\\v{R}", "\\v R" }, /*                 with caron */
+   { 340, LATEX_MACRO, "'R",   { "{\\'{R}}", "\\'{R}", "\\'R"    } }, /*               with acute */
+   { 342, LATEX_MACRO, "c{R}", { "{\\c{R}}", "\\c{R}", "\\c R"   } }, /*               with cedilla */
+   { 344, LATEX_MACRO, "v{R}", { "{\\v{R}}", "\\v{R}", "\\v R"   } }, /*               with caron */
  
                                            /* Latin Small r */
-   { 341, "{\\'{r}}", "\\'{r}", "\\'r"  }, /*               with acute */
-   { 343, "{\\c{r}}", "\\c{r}", "\\c r" }, /*               with cedilla */
-   { 345, "{\\v{r}}", "\\v{r}", "\\v r" }, /*               with caron */
+   { 341, LATEX_MACRO, "'r",   { "{\\'{r}}", "\\'{r}", "\\'r"    } }, /*               with acute */
+   { 343, LATEX_MACRO, "c{r}", { "{\\c{r}}", "\\c{r}", "\\c r"   } }, /*               with cedilla */
+   { 345, LATEX_MACRO, "v{r}", { "{\\v{r}}", "\\v{r}", "\\v r"   } }, /*               with caron */
 
                                            /* Latin Capital S */
-   { 346, "{\\'{S}}", "\\'{S}", "\\'S"  }, /*               with acute */
-   { 348, "{\\^{S}}", "\\^{S}", "\\^S"  }, /*               with circumflex */
-   { 350, "{\\c{S}}", "\\c{S}", "\\c S" }, /*               with cedilla */
-   { 352, "{\\v{S}}", "\\v{S}", "\\v S" }, /*               with caron */
+   { 346, LATEX_MACRO, "'{S}", { "{\\'{S}}", "\\'{S}", "\\'S"    } }, /*               with acute */
+   { 348, LATEX_MACRO, "^{S}", { "{\\^{S}}", "\\^{S}", "\\^S"    } }, /*               with circumflex */
+   { 350, LATEX_MACRO, "c{S}", { "{\\c{S}}", "\\c{S}", "\\c S"   } }, /*               with cedilla */
+   { 352, LATEX_MACRO, "v{S}", { "{\\v{S}}", "\\v{S}", "\\v S"   } }, /*               with caron */
 
                                            /* Latin Small s */
-   { 347, "{\\'{s}}","\\'{s}",  "\\'s"  }, /*               with acute */
-   { 349, "{\\^{s}}","\\^{s}",  "\\^s"  }, /*               with circumflex */
-   { 351, "{\\c{s}}","\\c{s}",  "\\c s" }, /*               with cedilla */
-   { 353, "{\\v{s}}","\\v{s}",  "\\v s" }, /*               with caron */
+   { 347, LATEX_MACRO, "'{s}", { "{\\'{s}}", "\\'{s}", "\\'s"    } }, /*               with acute */
+   { 349, LATEX_MACRO, "^{s}", { "{\\^{s}}", "\\^{s}", "\\^s"    } }, /*               with circumflex */
+   { 351, LATEX_MACRO, "c{s}", { "{\\c{s}}", "\\c{s}", "\\c s"   } }, /*               with cedilla */
+   { 353, LATEX_MACRO, "v{s}", { "{\\v{s}}", "\\v{s}", "\\v s"   } }, /*               with caron */
 
                                            /* Latin Capital T */
-   { 354, "{\\c{T}}", "\\c{T}", ""      }, /*               with cedilla */
-   { 356, "{\\v{T}}", "\\v{T}", ""      }, /*               with caron */
-/* { 358, "",         "",       ""      },*//*               with stroke */
+   { 354, LATEX_MACRO, "c{T}", { "{\\c{T}}", "\\c{T}", NULL      } }, /*               with cedilla */
+   { 356, LATEX_MACRO, "v{T}", { "{\\v{T}}", "\\v{T}", NULL      } }, /*               with caron */
+/* { 358, LATEX_MACRO, "",     { NULL,       NULL,     NULL      } },*//*              with stroke */
 
                                            /* Latin Small t */
-   { 355, "{\\c{t}}", "\\c{t}", ""      }, /*               with cedilla */
-   { 357, "{\\v{t}}", "\\v{t}", ""      }, /*               with caron */
-/* { 359, "",         "",       ""      },*//*               with stroke */
+   { 355, LATEX_MACRO, "c{T}", { "{\\c{t}}", "\\c{t}", NULL      } }, /*               with cedilla */
+   { 357, LATEX_MACRO, "v{T}", { "{\\v{t}}", "\\v{t}", NULL      } }, /*               with caron */
+/* { 359, LATEX_MACRO, "",     { NULL,       NULL,     NULL      } },*//*              with stroke */
 
-   { 223, "{\\ss}",  "\\ss",    ""      }, /* German sz ligature, "sharp s" */
+   { 223, LATEX_MACRO, "ss",   { "{\\ss}",   "\\ss",   NULL      } }, /* German sz ligature, "sharp s" */
 
                                            /* Latin Capital U */
-   { 217, "{\\`U}",  "\\`{U}",  "\\`U"  }, /*               with grave */
-   { 218, "{\\'U}",  "\\'{U}",  "\\'U"  }, /*               with acute */
-   { 219, "{\\^U}",  "\\^{U}",  "\\^U"  }, /*               with circumflex */
-   { 220, "{\\\"U}", "\\\"{U}", "\\\"U" }, /*               with diaeresis */
-   { 360, "{\\~{U}}","\\~{U}",  "\\~U"  }, /*               with tilde */
-   { 362, "{\\={U}}","\\={U}",  "\\=U"  }, /*               with macron */
-   { 364, "{\\u{U}}","\\u{U}",  "\\u U" }, /*               with breve */
-   { 366, "{\\r{U}}","\\r{U}",  "\\r U" }, /*               with ring above */
-   { 368, "{\\H{U}}","\\H{U}",  "\\H U" }, /*               with double acute */
-   { 370, "{\\k{U}}","\\k{U}",  "\\k U" }, /*               with ogonek */
-   { 467, "{\\v{U}}","\\v{U}",  "\\v U" }, /*               with caron */
+   { 217, LATEX_MACRO, "`U",   { "{\\`U}",   "\\`{U}", "\\`U"    } }, /*               with grave */
+   { 218, LATEX_MACRO, "'U",   { "{\\'U}",   "\\'{U}", "\\'U"    } }, /*               with acute */
+   { 219, LATEX_MACRO, "^U",   { "{\\^U}",   "\\^{U}", "\\^U"    } }, /*               with circumflex */
+   { 220, LATEX_MACRO, "\"U",  { "{\\\"U}",  "\\\"{U}","\\\"U"   } }, /*               with diaeresis */
+   { 360, LATEX_MACRO, "~{U}", { "{\\~{U}}", "\\~{U}", "\\~U"    } }, /*               with tilde */
+   { 362, LATEX_MACRO, "={U}", { "{\\={U}}", "\\={U}", "\\=U"    } }, /*               with macron */
+   { 364, LATEX_MACRO, "u{U}", { "{\\u{U}}", "\\u{U}", "\\u U"   } }, /*               with breve */
+   { 366, LATEX_MACRO, "r{U}", { "{\\r{U}}", "\\r{U}", "\\r U"   } }, /*               with ring above */
+   { 368, LATEX_MACRO, "H{U}", { "{\\H{U}}", "\\H{U}", "\\H U"   } }, /*               with double acute */
+   { 370, LATEX_MACRO, "k{U}", { "{\\k{U}}", "\\k{U}", "\\k U"   } }, /*               with ogonek */
+   { 467, LATEX_MACRO, "v{U}", { "{\\v{U}}", "\\v{U}", "\\v U"   } }, /*               with caron */
 
                                            /* Latin Small u */
-   { 249, "{\\`u}",  "\\`{u}",  "\\`u"  }, /*               with grave */
-   { 250, "{\\'u}",  "\\'{u}",  "\\'u"  }, /*               with acute */
-   { 251, "{\\^u}",  "\\^{u}",  "\\^u"  }, /*               with circumflex */
-   { 252, "{\\\"u}", "\\\"{u}", "\\\"u" }, /*               with diaeresis */
-   { 361, "{\\~{u}}","\\~{u}",  ""      }, /*               with tilde */
-   { 363, "{\\={u}}","\\={u}",  "\\=u"  }, /*               with macron */
-   { 365, "{\\u{u}}","\\u{u}",  "\\u u" }, /*               with breve */
-   { 367, "{\\r{u}}","\\r{u}",  "\\r u" }, /*               with ring above */
-   { 369, "{\\H{u}}","\\H{u}",  "\\H u" }, /*               with double acute */
-   { 371, "{\\k{u}}","\\k{u}",  "\\k u" }, /*               with ogonek */
-   { 468, "{\\v{u}}","\\v{u}",  "\\v u" }, /*               with caron */
+   { 249, LATEX_MACRO, "`u",   { "{\\`u}",   "\\`{u}", "\\`u"    } }, /*               with grave */
+   { 250, LATEX_MACRO, "'u",   { "{\\'u}",   "\\'{u}", "\\'u"    } }, /*               with acute */
+   { 251, LATEX_MACRO, "^u",   { "{\\^u}",   "\\^{u}", "\\^u"    } }, /*               with circumflex */
+   { 252, LATEX_MACRO, "\"u",  { "{\\\"u}",  "\\\"{u}","\\\"u"   } }, /*               with diaeresis */
+   { 361, LATEX_MACRO, "~{u}", { "{\\~{u}}", "\\~{u}", NULL      } }, /*               with tilde */
+   { 363, LATEX_MACRO, "={u}", { "{\\={u}}", "\\={u}", "\\=u"    } }, /*               with macron */
+   { 365, LATEX_MACRO, "u{u}", { "{\\u{u}}", "\\u{u}", "\\u u"   } }, /*               with breve */
+   { 367, LATEX_MACRO, "r{u}", { "{\\r{u}}", "\\r{u}", "\\r u"   } }, /*               with ring above */
+   { 369, LATEX_MACRO, "H{u}", { "{\\H{u}}", "\\H{u}", "\\H u"   } }, /*               with double acute */
+   { 371, LATEX_MACRO, "k{u}", { "{\\k{u}}", "\\k{u}", "\\k u"   } }, /*               with ogonek */
+   { 468, LATEX_MACRO, "v{u}", { "{\\v{u}}", "\\v{u}", "\\v u"   } }, /*               with caron */
 
                                            /* Latin Capital W */
-   { 372, "{\\^{W}}", "\\^{W}", "\\^W"  }, /*               with circumflex */
+   { 372, LATEX_MACRO, "^{W}", { "{\\^{W}}", "\\^{W}", "\\^W"    } }, /*               with circumflex */
 
                                            /* Latin Small w */
-   { 373, "{\\^{w}}", "\\^{w}", "\\^w"  }, /*               with circumflex */
+   { 373, LATEX_MACRO, "^{w}", { "{\\^{w}}", "\\^{w}", "\\^w"    } }, /*               with circumflex */
 
                                            /* Latin Capital Y */
-   { 221, "{\\'{Y}}","\\'{Y}", "\\'Y"   }, /*               with acute */
-   { 374, "{\\^{Y}}","\\^{Y}", "\\^Y"   }, /*               with circumflex */
-   { 376, "{\\\"{Y}}","\\\"{Y}","\\\"Y" }, /*               with diaeresis */
+   { 221, LATEX_MACRO, "'{Y}", { "{\\'{Y}}", "\\'{Y}", "\\'Y"    } }, /*               with acute */
+   { 374, LATEX_MACRO, "^{Y}", { "{\\^{Y}}", "\\^{Y}", "\\^Y"    } }, /*               with circumflex */
+   { 376, LATEX_MACRO, "\"{Y}",{ "{\\\"{Y}}","\\\"{Y}","\\\"Y"   } }, /*               with diaeresis */
 
                                            /* Latin Small y */
-   { 253, "{\\'y}",  "\\'{y}", "\\'y"   }, /*               with acute */
-   { 255, "{\\\"y}", "\\\"{y}","\\\"y"  }, /*               with diaeresis */
-   { 375, "{\\^{y}}","\\^{y}", "\\^y"   }, /*               with circumflex */
+   { 253, LATEX_MACRO, "'y",   { "{\\'y}",  "\\'{y}", "\\'y"     } }, /*               with acute */
+   { 255, LATEX_MACRO, "\"y",  { "{\\\"y}", "\\\"{y}","\\\"y"    } }, /*               with diaeresis */
+   { 375, LATEX_MACRO, "^{y}", { "{\\^{y}}","\\^{y}", "\\^y"     } }, /*               with circumflex */
 
                                            /* Latin Capital Z */
-   { 377, "{\\'{Z}}","\\'{Z}", "\\'Z"   }, /*               with acute */
-   { 379, "{\\.{Z}}","\\.{Z}", "\\.Z"   }, /*               with dot above */
-   { 381, "{\\v{Z}}","\\v{Z}", "\\v Z"  }, /*               with caron */
+   { 377, LATEX_MACRO, "'{Z}", { "{\\'{Z}}","\\'{Z}", "\\'Z"     } }, /*               with acute */
+   { 379, LATEX_MACRO, ".{Z}", { "{\\.{Z}}","\\.{Z}", "\\.Z"     } }, /*               with dot above */
+   { 381, LATEX_MACRO, "v{Z}", { "{\\v{Z}}","\\v{Z}", "\\v Z"    } }, /*               with caron */
 
                                            /* Latin Small z */
-   { 378, "{\\'{z}}","\\'{z}", "\\'z"   }, /*               with acute */
-   { 380, "{\\.{z}}","\\.{z}", "\\.z"   }, /*               with dot above */
-   { 382, "{\\v{z}}","\\v{z}", "\\v z"  }, /*               with caron */
+   { 378, LATEX_MACRO, "'{z}", { "{\\'{z}}","\\'{z}", "\\'z"     } }, /*               with acute */
+   { 380, LATEX_MACRO, ".{z}", { "{\\.{z}}","\\.{z}", "\\.z"     } }, /*               with dot above */
+   { 382, LATEX_MACRO, "v{z}", { "{\\v{z}}","\\v{z}", "\\v z"    } }, /*               with caron */
 
 
 				/* Needs to be before \nu */
-   { 8203, "$\\null$", "\\null", "" },           /* No space &#x200B; */
+   { 8203,LATEX_MATH, "null",  { "$\\null$", "\\null", NULL      } }, /* No space &#x200B; */
 
-   { 913, "$\\Alpha$", "\\Alpha", ""       }, /*GREEKCAPITALLETTERALPHA*/
-   /* 902 = GREEKCAPITALLETTERALPHAWITHTONOS*/
-   { 914, "$\\Beta$",  "\\Beta", ""       }, /*GREEKCAPITALLETTERBETA*/
-   { 915, "$\\Gamma$", "\\Gamma", ""       }, /*GREEKCAPITALLETTERGAMMA*/
-   { 916, "$\\Delta$", "\\Delta", ""       }, /*GREEKCAPITALLETTERDELTA*/
-   { 917, "$\\Epsilon$", "\\Epsilon", ""     }, /*GREEKCAPITALLETTEREPSILON*/
-   /* 904 = GREEKCAPITALLETTEREPSILONWITHTONOS*/
-   { 918, "$\\Zeta$", "\\Zeta", ""        }, /*GREEKCAPITALLETTERZETA*/
-   { 919, "$\\Eta$", "\\Eta", ""         }, /*GREEKCAPITALLETTERETA*/
-   /* 905 = GREEKCAPITALLETTERETAWITHTONOS*/
-   { 920, "$\\Theta$", "\\Theta", ""       }, /*GREEKCAPITALLETTERTHETA*/
-   { 921, "$\\Iota$", "\\Iota", ""        }, /*GREEKCAPITALLETTERIOTA*/
-   /* 938 = GREEKCAPITALLETTERIOTAWITHDIALYTIKA*/
-   /* 906 = GREEKCAPITALLETTERIOTAWITHTONOS*/
-   { 922, "$\\Kappa$", "\\Kappa", ""       }, /*GREEKCAPITALLETTERKAPPA*/
-   { 923, "$\\Lambda$", "\\Lambda", ""      }, /*GREEKCAPITALLETTERLAMDA*/
-   { 924, "$\\Mu$", "\\Mu", ""          }, /*GREEKCAPITALLETTERMU*/
-   { 925, "$\\Nu$", "\\Nu", ""          }, /*GREEKCAPITALLETTERNU*/
-   { 926, "$\\Xi$", "\\Xi", ""          }, /*GREEKCAPITALLETTERXI*/
-   { 927, "$\\Omicron$", "\\Omicron", ""     }, /*GREEKCAPITALLETTEROMICRON*/
-   /* 908 = GREEKCAPITALLETTEROMICRONWITHTONOS*/
-   { 928, "$\\Pi$", "\\Pi", ""          }, /*GREEKCAPITALLETTERPI*/
-   { 929, "$\\Rho$", "\\Rho", ""         }, /*GREEKCAPITALLETTERRHO*/
-   { 931, "$\\Sigma$", "\\Sigma", ""       }, /*GREEKCAPITALLETTERSIGMA*/
-   { 932, "$\\Tau$", "\\Tau", ""         }, /*GREEKCAPITALLETTERTAU*/
-   { 933, "$\\Upsilon$", "\\Upsilon", ""     }, /*GREEKCAPITALLETTERUPSILON*/
-   /* 939 = GREEKCAPITALLETTERUPSILONWITHDIALYTIKA*/
-   /* 910 = GREEKCAPITALLETTERUPSILONWITHTONOS*/
-   { 934, "$\\Phi$", "\\Phi", ""         }, /*GREEKCAPITALLETTERPHI*/
-   { 935, "$\\Chi$", "\\Chi", ""         }, /*GREEKCAPITALLETTERCHI*/
-   { 936, "$\\Psi$", "\\Psi", ""         }, /*GREEKCAPITALLETTERPSI*/
-   { 937, "$\\Omega$", "\\Omega", ""       }, /*GREEKCAPITALLETTEROMEGA*/
-   /* 911, = GREEKCAPITALLETTEROMEGAWITHTONOS*/
+   { 913, LATEX_MATH, "Alpha",   { "$\\Alpha$",   "\\Alpha",    NULL    } }, /*GREEK CAPITAL LETTERALPHA*/
+   { 914, LATEX_MATH, "Beta",    { "$\\Beta$",    "\\Beta",     NULL    } }, /*GREEK CAPITAL LETTERBETA*/
+   { 915, LATEX_MATH, "Gamma",   { "$\\Gamma$",   "\\Gamma",    NULL    } }, /*GREEK CAPITAL LETTERGAMMA*/
+   { 916, LATEX_MATH, "Delta",   { "$\\Delta$",   "\\Delta",    NULL    } }, /*GREEK CAPITAL LETTERDELTA*/
+   { 917, LATEX_MATH, "Epsilon", { "$\\Epsilon$", "\\Epsilon",  NULL    } }, /*GREEK CAPITAL LETTEREPSILON*/
+   { 918, LATEX_MATH, "Zeta",    { "$\\Zeta$",     "\\Zeta",    NULL    } }, /*GREEK CAPITAL LETTERZETA*/
+   { 919, LATEX_MATH, "Eta",     { "$\\Eta$",      "\\Eta",     NULL    } }, /*GREEK CAPITAL LETTERETA*/
+   { 920, LATEX_MATH, "Theta",   { "$\\Theta$",    "\\Theta",   NULL    } }, /*GREEK CAPITAL LETTERTHETA*/
+   { 921, LATEX_MATH, "Iota",    { "$\\Iota$",     "\\Iota",    NULL    } }, /*GREEK CAPITAL LETTERIOTA*/
+   { 922, LATEX_MATH, "Kappa",   { "$\\Kappa$",    "\\Kappa",   NULL    } }, /*GREEK CAPITAL LETTERKAPPA*/
+   { 923, LATEX_MATH, "Lambda",  { "$\\Lambda$",   "\\Lambda",  NULL    } }, /*GREEK CAPITAL LETTERLAMDA*/
+   { 924, LATEX_MATH, "Mu",      { "$\\Mu$",       "\\Mu",      NULL    } }, /*GREEK CAPITAL LETTERMU*/
+   { 925, LATEX_MATH, "Nu",      { "$\\Nu$",       "\\Nu",      NULL    } }, /*GREEK CAPITAL LETTERNU*/
+   { 926, LATEX_MATH, "Xi",      { "$\\Xi$",       "\\Xi",      NULL    } }, /*GREEK CAPITAL LETTERXI*/
+   { 927, LATEX_MATH, "Omicron", { "$\\Omicron$",  "\\Omicron", NULL    } }, /*GREEK CAPITAL LETTEROMICRON*/
+   { 928, LATEX_MATH, "Pi",      { "$\\Pi$",       "\\Pi",      NULL    } }, /*GREEK CAPITAL LETTERPI*/
+   { 929, LATEX_MATH, "Rho",     { "$\\Rho$",      "\\Rho",     NULL    } }, /*GREEK CAPITAL LETTERRHO*/
+   { 931, LATEX_MATH, "Sigma",   { "$\\Sigma$",    "\\Sigma",   NULL    } }, /*GREEK CAPITAL LETTERSIGMA*/
+   { 932, LATEX_MATH, "Tau",     { "$\\Tau$",      "\\Tau",     NULL    } }, /*GREEK CAPITAL LETTERTAU*/
+   { 933, LATEX_MATH, "Upsilon", { "$\\Upsilon$",  "\\Upsilon", NULL    } }, /*GREEK CAPITAL LETTERUPSILON*/
+   { 934, LATEX_MATH, "Phi",     { "$\\Phi$",      "\\Phi",     NULL    } }, /*GREEK CAPITAL LETTERPHI*/
+   { 935, LATEX_MATH, "Chi",     { "$\\Chi$",      "\\Chi",     NULL    } }, /*GREEK CAPITAL LETTERCHI*/
+   { 936, LATEX_MATH, "Psi",     { "$\\Psi$",      "\\Psi",     NULL    } }, /*GREEK CAPITAL LETTERPSI*/
+   { 937, LATEX_MATH, "Omega",   { "$\\Omega$",    "\\Omega",   NULL    } }, /*GREEK CAPITAL LETTEROMEGA*/
+   /* 902 = GREEK CAPITAL LETTER ALPHAWITHTONOS*/
+   /* 904 = GREEK CAPITAL LETTER EPSILONWITHTONOS*/
+   /* 905 = GREEK CAPITAL LETTER ETAWITHTONOS*/
+   /* 938 = GREEK CAPITAL LETTER IOTAWITHDIALYTIKA*/
+   /* 906 = GREEK CAPITAL LETTER IOTAWITHTONOS*/
+   /* 908 = GREEK CAPITAL LETTER OMICRONWITHTONOS*/
+   /* 939 = GREEK CAPITAL LETTER UPSILONWITHDIALYTIKA*/
+   /* 910 = GREEK CAPITAL LETTER UPSILONWITHTONOS*/
+   /* 911, = GREEK CAPITAL LETTER OMEGAWITHTONOS*/
 
-   { 945, "$\\alpha$", "\\alpha", ""       }, /*GREEKSMALLLETTERALPHA*/
-   /* 940 = GREEKSMALLLETTERALPHAWITHTONOS*/
-   { 946, "$\\beta$", "\\beta", ""        }, /*GREEKSMALLLETTERBETA*/
-   { 968, "$\\psi$", "\\psi", ""         }, /*GREEKSMALLLETTERPSI*/
-   { 948, "$\\delta$", "\\delta", ""       }, /*GREEKSMALLLETTERDELTA*/
-   { 949, "$\\epsilon$", "\\epsilon", ""     }, /*GREEKSMALLLETTEREPSILON*/
-   /* 941 = GREEKSMALLLETTEREPSILONWITHTONOS*/
-   { 966, "$\\phi$", "\\phi", ""         }, /*GREEKSMALLLETTERPHI*/
-   { 947, "$\\gamma$", "\\gamma", ""       }, /*GREEKSMALLLETTERGAMMA*/
-   { 951, "$\\eta$", "\\eta", ""         }, /*GREEKSMALLLETTERETA*/
-   /* 942 = GREEKSMALLLETTERETAWITHTONOS */
-   { 953, "$\\iota$", "\\iota", ""        }, /*GREEKSMALLLETTERIOTA*/
-   /* 912 = GREEKSMALLLETTERIOTAWITHDIALYTIKAANDTONOS*/
-   /* 943 = GREEKSMALLLETTERIOTAWITHTONOS */
-   /* 970 = GREEKSMALLLETTERIOTAWITHDIALYTIKA */
-   { 958, "$\\xi$", "\\xi", ""          }, /*GREEKSMALLLETTERXI*/
-   { 954, "$\\kappa$", "\\kappa" , ""      }, /*GREEKSMALLLETTERKAPPA*/
-   { 955, "$\\lambda$", "\\lambda", ""      }, /*GREEKSMALLLETTERLAMDA*/
-   { 956, "$\\mu$", "\\mu", ""          }, /*GREEKSMALLLETTERMU*/
-   { 957, "$\\nu$", "\\nu", ""          }, /*GREEKSMALLLETTERNU*/
-   { 959, "$\\omicron$", "\\omicron", ""     }, /*GREEKSMALLLETTEROMICRON*/
-   /* 972 = GREEKSMALLLETTEROMICRONWITHTONOS*/
-   { 960, "$\\pi$", "\\pi", ""          }, /*GREEKSMALLLETTERPI*/
-   { 961, "$\\rho$", "\\rho", ""         }, /*GREEKSMALLLETTERRHO*/
-   { 963, "$\\sigma$", "\\sigma", ""       }, /*GREEKSMALLLETTERSIGMA*/
-   { 964, "$\\tau$", "\\tau", ""         }, /*GREEKSMALLLETTERTAU*/
-   { 952, "$\\theta$", "\\theta", ""       }, /*GREEKSMALLLETTERTHETA*/
-   { 969, "$\\omega$", "\\omega", ""       }, /*GREEKSMALLLETTEROMEGA*/
-   /* 974 = GREEKSMALLLETTEROMEGAWITHTONOS*/
-   { 967, "$\\chi$", "\\chi", ""         }, /*GREEKSMALLLETTERCHI*/
-   { 965, "$\\upsilon$", "\\upsilon", ""     }, /*GREEKSMALLLETTERUPSILON*/
-   /* 973 = GREEKSMALLLETTERUPSILONWITHTONOS*/
-   /* 971 = GREEKSMALLLETTERUPSILONWITHDIALYTIKA*/
-   /* 944 = GREEKSMALLLETTERUPSILONWITHDIALYTIKAANDTONOS*/
-   { 950, "$\\zeta$", "\\zeta", ""        },  /*GREEKSMALLLETTERZETA*/
+   { 945, LATEX_MATH, "alpha",   { "$\\alpha$",    "\\alpha",   NULL    } }, /*GREEK SMALL LETTER ALPHA*/
+   { 946, LATEX_MATH, "beta",    { "$\\beta$",     "\\beta",    NULL    } }, /*GREEK SMALL LETTER BETA*/
+   { 968, LATEX_MATH, "psi",     { "$\\psi$",      "\\psi",     NULL    } }, /*GREEK SMALL LETTER PSI*/
+   { 948, LATEX_MATH, "delta",   { "$\\delta$",    "\\delta",   NULL    } }, /*GREEK SMALL LETTER DELTA*/
+   { 949, LATEX_MATH, "epsilon", { "$\\epsilon$",  "\\epsilon", NULL    } }, /*GREEK SMALL LETTER EPSILON*/
+   { 966, LATEX_MATH, "phi",     { "$\\phi$",      "\\phi",     NULL    } }, /*GREEK SMALL LETTER PHI*/
+   { 947, LATEX_MATH, "gamma",   { "$\\gamma$", "\\gamma", NULL }   }, /*GREEK SMALL LETTER GAMMA*/
+   { 951, LATEX_MATH, "eta",     { "$\\eta$", "\\eta", NULL }     }, /*GREEK SMALL LETTER ETA*/
+   { 953, LATEX_MATH, "iota",    { "$\\iota$", "\\iota", NULL }    }, /*GREEK SMALL LETTER IOTA*/
+   { 958, LATEX_MATH, "xi",      { "$\\xi$", "\\xi", NULL }      }, /*GREEK SMALL LETTER XI*/
+   { 954, LATEX_MATH, "kappa",   { "$\\kappa$", "\\kappa" , NULL }  }, /*GREEK SMALL LETTER KAPPA*/
+   { 955, LATEX_MATH, "lambda",  { "$\\lambda$", "\\lambda", NULL }  }, /*GREEK SMALL LETTER LAMDA*/
+   { 956, LATEX_MATH, "mu",      { "$\\mu$", "\\mu", NULL }      }, /*GREEK SMALL LETTER MU*/
+   { 957, LATEX_MATH, "nu",      { "$\\nu$", "\\nu", NULL }      }, /*GREEK SMALL LETTER NU*/
+   { 959, LATEX_MATH, "omicron", { "$\\omicron$", "\\omicron", NULL  }    }, /*GREEK SMALL LETTER OMICRON*/
+   { 960, LATEX_MATH, "pi",      { "$\\pi$", "\\pi", NULL }      }, /*GREEK SMALL LETTER PI*/
+   { 961, LATEX_MATH, "rho",     { "$\\rho$", "\\rho", NULL }     }, /*GREEK SMALL LETTER RHO*/
+   { 963, LATEX_MATH, "sigma",   { "$\\sigma$", "\\sigma", NULL }   }, /*GREEK SMALL LETTER SIGMA*/
+   { 964, LATEX_MATH, "tau",     { "$\\tau$", "\\tau", NULL }     }, /*GREEK SMALL LETTER TAU*/
+   { 952, LATEX_MATH, "theta",   { "$\\theta$", "\\theta", NULL }   }, /*GREEK SMALL LETTER THETA*/
+   { 969, LATEX_MATH, "omega",   { "$\\omega$", "\\omega", NULL }   }, /*GREEK SMALL LETTER OMEGA*/
+   { 967, LATEX_MATH, "chi",     { "$\\chi$", "\\chi", NULL }     }, /*GREEK SMALL LETTER CHI*/
+   { 965, LATEX_MATH, "upsilon", { "$\\upsilon$", "\\upsilon", NULL } }, /*GREEK SMALL LETTER UPSILON*/
+   { 950, LATEX_MATH, "zeta",    { "$\\zeta$", "\\zeta", NULL }    },  /*GREEK SMALL LETTER ZETA*/
+   /* 940 = GREEK SMALL LETTER ALPHAWITHTONOS*/
+   /* 941 = GREEK SMALL LETTER EPSILONWITHTONOS*/
+   /* 942 = GREEK SMALL LETTER ETAWITHTONOS */
+   /* 912 = GREEK SMALL LETTER IOTAWITHDIALYTIKAANDTONOS*/
+   /* 943 = GREEK SMALL LETTER IOTAWITHTONOS */
+   /* 970 = GREEK SMALL LETTER IOTAWITHDIALYTIKA */
+   /* 972 = GREEK SMALL LETTER OMICRONWITHTONOS*/
+   /* 974 = GREEK SMALL LETTER OMEGAWITHTONOS*/
+   /* 973 = GREEK SMALL LETTER UPSILONWITHTONOS*/
+   /* 971 = GREEK SMALL LETTER UPSILONWITHDIALYTIKA*/
+   /* 944 = GREEK SMALL LETTER UPSILONWITHDIALYTIKAANDTONOS*/
 
-   { 181, "{\\textmu}", "\\textmu", "$\\mu$" }, /* 181=micro sign, techically &#xB5; */
+   { 181, LATEX_MACRO, "textmu", { "{\\textmu}", "\\textmu", "$\\mu$" } }, /* 181=micro sign, techically &#xB5; */
 
 /* Make sure that these don't stomp on other latex things above */
 
-   { 8242, "{\\textasciiacutex}", "\\textasciiacutex", "$'$" },  /* Prime symbol &#x2032; */
-   { 180, "{\\textasciiacute}", "\\textasciiacute", "\\'" }, /* acute accent &#xB4; */
-/*   { 769,  "\\'",    "", "" },*/  /* Acute accent &#x0301;--apply to next char */
-
-   { 8243, "{\\textacutedbl}", "\\textacutedbl", "$''$" },  /* Double prime &#x2033; */
-   { 8245, "{\\textasciigrave}", "\\textasciigrave", "\\`" }, /* Grave accent &#x2035; */
-/*   { 768,  "\\`",    "", "" },*/  /* Grave accent &#x0300;--apply to next char */
+   { 8242, LATEX_MACRO, "textasciiacutex",   { "{\\textasciiacutex}",   "\\textasciiacutex",     "$'$"                 } }, /* Prime symbol &#x2032; */
+   { 180,  LATEX_MACRO, "textasciiacute",    { "{\\textasciiacute}",    "\\textasciiacute",      "\\'"                 } }, /* acute accent &#xB4; */
+   { 8243, LATEX_MACRO, "textacutedbl",      { "{\\textacutedbl}",      "\\textacutedbl",        "$''$"                } }, /* Double prime &#x2033; */
+   { 8245, LATEX_MACRO, "textasciigrave",    { "{\\textasciigrave}",    "\\textasciigrave",      "\\`"                 } }, /* Grave accent &#x2035; */
+/* { 768,  LATEX_MACRO, "`",                 { "\\`",                   NULL,                    NULL                  } },*//* Grave accent &#x0300;--apply to next char */
+/* { 769,  LATEX_MACRO, "'",                 { "\\'",                   NULL,                    NULL                  } },*//* Acute accent &#x0301;--apply to next char */
 
-   { 8963, "{\\textasciicircum}", "\\textasciicircum", ""} , /* &#x2303; */
-   { 184,  "{\\textasciicedilla}", "\\textasciicedilla", "" },  /* cedilla &#xB8; */
-   { 168,  "{\\textasciidieresis}", "\\textasciidieresis", "" }, /* dieresis &#xA8; */
-   { 175,  "{\\textasciimacron}", "\\textasciimacron", "" }, /* macron &#xAF; */
+   { 8963, LATEX_MACRO, "textasciicircum",   { "{\\textasciicircum}",   "\\textasciicircum",     NULL                  } }, /* &#x2303; */
+   { 184,  LATEX_MACRO, "textasciicedilla",  { "{\\textasciicedilla}",  "\\textasciicedilla",    NULL                  } }, /* cedilla &#xB8; */
+   { 168,  LATEX_MACRO, "textasciidieresis", { "{\\textasciidieresis}", "\\textasciidieresis",   NULL                  } }, /* dieresis &#xA8; */
+   { 175,  LATEX_MACRO, "textasciimacron",   { "{\\textasciimacron}",   "\\textasciimacron",     NULL                  } }, /* macron &#xAF; */
 
-   { 8593, "{\\textuparrow}", "\\textuparrow", "" },    /* Up arrow &#x2191; */
-   { 8595, "{\\textdownarrow}", "\\textdownarrow", "" },  /* Down arrow &#x2193; */
-   { 8594, "{\\textrightarrow}", "\\textrightarrow", "" }, /* Right arrow &#x2192; */
-   { 8592, "{\\textleftarrow}", "\\textleftarrow", "" },  /* Left arrow &#x2190; */
-   { 12296, "{\\textlangle}", "\\textlangle", "" } ,   /* L-angle &#x3008; */
-   { 12297, "{\\textrangle}", "\\textrangle", "" } ,   /* L-angle &#x3009; */
+   { 8593, LATEX_MACRO, "textuparrow",       { "{\\textuparrow}",       "\\textuparrow",         NULL                  } }, /* Up arrow &#x2191; */
+   { 8595, LATEX_MACRO, "textdownarrow",     { "{\\textdownarrow}",     "\\textdownarrow",       NULL                  } }, /* Down arrow &#x2193; */
+   { 8594, LATEX_MACRO, "textrightarrow",    { "{\\textrightarrow}",    "\\textrightarrow",      NULL                  } }, /* Right arrow &#x2192; */
+   { 8592, LATEX_MACRO, "textleftarrow",     { "{\\textleftarrow}",     "\\textleftarrow",       NULL                  } }, /* Left arrow &#x2190; */
+   { 12296,LATEX_MACRO, "textlangle",        { "{\\textlangle}",        "\\textlangle",          NULL                  } }, /* L-angle &#x3008; */
+   { 12297,LATEX_MACRO, "textrangle",        { "{\\textrangle}",        "\\textrangle",          NULL                  } }, /* L-angle &#x3009; */
 
-   { 166, "{\\textbrokenbar}", "\\textbrokenbar", "" }, /* Broken vertical bar &#xA6; */
-   { 167, "{\\textsection}", "\\textsection", "\\S{}" },   /* Section sign, &#xA7; */
-   { 170, "{\\textordfeminine}", "\\textordfeminine", "$^a$" }, /* &#xAA; */
-   { 172, "{\\textlnot}", "\\textlnot", "" },      /* Lnot &#xAC; */
-   { 182, "{\\textparagraph}", "\\textparagraph", "" }, /* Paragraph sign &#xB6; */
-   { 183, "{\\textperiodcentered}", "\\textperiodcentered", "" }, /* Period-centered &#xB7; */
-   { 186, "{\\textordmasculine}", "\\textordmasculine", "" }, /* &#xBA; */
-   { 8214, "{\\textbardbl}", "\\textbardbl", "" },   /* Double vertical bar &#x2016; */
-   { 8224, "{\\textdagger}", "\\textdagger", "" },   /* Dagger &#x2020; */
-   { 8225, "{\\textdaggerdbl}", "\\textdaggerdbl", "" },/* Double dagger &x2021; */
-   { 8226, "{\\textbullet}", "\\textbullet", "" },   /* Bullet &#x2022; */
-   { 8494, "{\\textestimated}", "\\textestimated", "" },/* Estimated &#x212E; */
-   { 9526, "{\\textopenbullet}", "\\textopenbullet", "" },/* &#x2536; */
+   { 166,  LATEX_MACRO, "textbrokenbar",     { "{\\textbrokenbar}",     "\\textbrokenbar",       NULL                  } }, /* Broken vertical bar &#xA6; */
+   { 167,  LATEX_MACRO, "textsection",       { "{\\textsection}",       "\\textsection",         "\\S{}"               } }, /* Section sign, &#xA7; */
+   { 170,  LATEX_MACRO, "textordfeminine",   { "{\\textordfeminine}",   "\\textordfeminine",     "$^a$"                } }, /* &#xAA; */
+   { 172,  LATEX_MACRO, "textlnot",          { "{\\textlnot}",          "\\textlnot",            NULL                  } }, /* Lnot &#xAC; */
+   { 182,  LATEX_MACRO, "textparagraph",     { "{\\textparagraph}",     "\\textparagraph",       NULL                  } }, /* Paragraph sign &#xB6; */
+   { 183,  LATEX_MACRO, "textperiodcentered",{ "{\\textperiodcentered}","\\textperiodcentered",  NULL                  } }, /* Period-centered &#xB7; */
+   { 186,  LATEX_MACRO, "textordmasculine",  { "{\\textordmasculine}",  "\\textordmasculine",    NULL                  } }, /* &#xBA; */
+   { 8214, LATEX_MACRO, "textbardbl",        { "{\\textbardbl}",        "\\textbardbl",          NULL                  } }, /* Double vertical bar &#x2016; */
+   { 8224, LATEX_MACRO, "textdagger",        { "{\\textdagger}",        "\\textdagger",          NULL                  } }, /* Dagger &#x2020; */
+   { 8225, LATEX_MACRO, "textdaggerdbl",     { "{\\textdaggerdbl}",     "\\textdaggerdbl",       NULL                  } }, /* Double dagger &x2021; */
+   { 8226, LATEX_MACRO, "textbullet",        { "{\\textbullet}",        "\\textbullet",          NULL                  } }, /* Bullet &#x2022; */
+   { 8494, LATEX_MACRO, "textestimated",     { "{\\textestimated}",     "\\textestimated",       NULL                  } }, /* Estimated &#x212E; */
+   { 9526, LATEX_MACRO, "textopenbullet",    { "{\\textopenbullet}",    "\\textopenbullet",      NULL                  } }, /* &#x2536; */
 
-   { 8220, "``", "{\\textquotedblleft}", "\\textquotedblleft" }, /* Opening double quote &#x201C; */
-   { 8221, "''", "{\\textquotedblright}","\\textquotedblright" }, /* Closing double quote &#x201D; */
-   { 8216, "`",  "{\\textquoteleft}", "\\textquoteleft" },    /* Opening single quote &#x2018; */
-   { 8217, "'",  "{\\textquoteright}", "\\textquoteright" },   /* Closing single quote &#x2019; */
-   { 8261, "{\\textlquill}", "\\textlquill", "" },         /* Left quill &#x2045; */
-   { 8262, "{\\textrquill}", "\\textrquill", "" },         /* Right quill &#x2046; */
+   { 8220, LATEX_COMBO, "``",                { "``",                    "{\\textquotedblleft}",  "\\textquotedblleft"  } }, /* Opening double quote &#x201C; */
+   { 8221, LATEX_COMBO, "''",                { "''",                    "{\\textquotedblright}", "\\textquotedblright" } }, /* Closing double quote &#x201D; */
+   { 8216, LATEX_COMBO, "`",                 { "`",                     "{\\textquoteleft}",     "\\textquoteleft"     } }, /* Opening single quote &#x2018; */
+   { 8217, LATEX_COMBO, "'",                 { "'",                     "{\\textquoteright}",    "\\textquoteright"    } }, /* Closing single quote &#x2019; */
+   { 8261, LATEX_MACRO, "textlquill",        { "{\\textlquill}",        "\\textlquill",          NULL                  } }, /* Left quill &#x2045; */
+   { 8262, LATEX_MACRO, "textrquill",        { "{\\textrquill}",        "\\textrquill",          NULL                  } }, /* Right quill &#x2046; */
 
-   { 8212, "---",     "{\\textemdash}", "\\textemdash" },     /* Em-dash &#x2014; */
-   { 8211, "--",      "{\\textendash}", "\\textendash" },     /* En-dash &#x2013; */
-   { 8230, "\\ldots", "{\\textellipsis}", "\\textellipsis" },   /* Ellipsis &#x2026; */
+   { 8212, LATEX_COMBO, "---",               { "---",                   "{\\textemdash}",        "\\textemdash"        } }, /* Em-dash &#x2014; */
+   { 8211, LATEX_COMBO, "--",                { "--",                    "{\\textendash}",        "\\textendash"        } }, /* En-dash &#x2013; */
+   { 8230, LATEX_MACRO, "ldots",             { "{\\ldots}",             "{\\textellipsis}",      "\\textellipsis"      } }, /* Ellipsis &#x2026; */
 
-   { 8194, "\\enspace", "\\hspace{.5em}", "" }, /* En-space &#x2002; */
-   { 8195, "\\emspace", "\\hspace{1em}",  "" }, /* Em-space &#x2003; */
-   { 8201, "\\thinspace", "", ""},              /* Thin space &#x2009; */
-   { 8203, "{\\textnospace}", "\\textnospace", "" },           /* No space &#x200B; */
-   { 9251, "{\\textvisiblespace}", "\\textvisiblespace", "" },      /* Visible space &#x2423; */
+   { 8194, LATEX_MACRO, "enspace",           { "{\\enspace}",           "\\hspace{.5em}",        NULL                  } }, /* En-space &#x2002; */
+   { 8195, LATEX_MACRO, "emspace",           { "{\\emspace}",           "\\hspace{1em}",         NULL                  } }, /* Em-space &#x2003; */
+   { 8201, LATEX_MACRO, "thinspace",         { "{\\thinspace}",         NULL,                    NULL                  } }, /* Thin space &#x2009; */
+   { 8203, LATEX_MACRO, "textnospace",       { "{\\textnospace}",       "\\textnospace",         NULL                  } }, /* No space &#x200B; */
+   { 9251, LATEX_MACRO, "textvisiblespace",  { "{\\textvisiblespace}",  "\\textvisiblespace",    NULL                  } }, /* Visible space &#x2423; */
 
-   { 215, "{\\texttimes}", "\\texttimes", "" }, /* Multiplication symbol &#xD7; */
-   { 247, "{\\textdiv}", "\\textdiv", "" },   /* Division symbol &#xF7; */
-   { 177, "{\\textpm}", "\\textpm", "" }, /* Plus-minus character &#B1; */
-   { 189, "{\\textonehalf}", "\\textonehalf", "" }, /* Vulgar fraction one half &#xBD; */
-   { 188, "{\\textonequarter}", "\\textonequarter", "" }, /* Vulgar fraction one quarter &#xBD; */
-   { 190, "{\\textthreequarters}", "\\textthreequarters", "" }, /* Vulgar fraction three quarters &#xBE; */
-   { 8240, "{\\texttenthousand}", "\\texttenthousand", "" }, /* Per thousand sign &#x2030; */
-   { 8241, "{\\textpertenthousand}", "\\textpertenthousand", "" }, /* Per ten thousand sign &#x2031;*/
-   { 8260, "{\\textfractionsolidus}", "\\textfractionsolidus", "" }, /* &x8260; */
-   { 8451, "{\\textcelcius}", "\\textcelcius", "" }, /* Celcicus &#x2103; */
-   { 8470, "{\\textnumero}", "\\textnumero", "" },  /* Numero symbol &#x2116; */
-   { 8486, "{\\textohm}", "\\textohm", "" }, /* Ohm symbol &#x2126; */
-   { 8487, "{\\textmho}", "\\textmho", "" }, /* Mho symbol &#x2127; */
-   { 8730, "{\\textsurd}", "\\textsurd", "" }, /* &#x221A; */
+   { 215,  LATEX_MACRO, "texttimes",         { "{\\texttimes}",         "\\texttimes",           NULL                  } }, /* Multiplication symbol &#xD7; */
+   { 247,  LATEX_MACRO, "textdiv",           { "{\\textdiv}",           "\\textdiv",             NULL                  } }, /* Division symbol &#xF7; */
+   { 177,  LATEX_MACRO, "textpm",            { "{\\textpm}",            "\\textpm",              NULL                  } }, /* Plus-minus character &#B1; */
+   { 189,  LATEX_MACRO, "textonehalf",       { "{\\textonehalf}",       "\\textonehalf",         NULL                  } }, /* Vulgar fraction one half &#xBD; */
+   { 188,  LATEX_MACRO, "textonequarter",    { "{\\textonequarter}",    "\\textonequarter",      NULL                  } }, /* Vulgar fraction one quarter &#xBD; */
+   { 190,  LATEX_MACRO, "textthreequarters", { "{\\textthreequarters}", "\\textthreequarters",   NULL                  } }, /* Vulgar fraction three quarters &#xBE; */
+   { 8240, LATEX_MACRO, "texttenthousand",   { "{\\texttenthousand}",   "\\texttenthousand",     NULL                  } }, /* Per thousand sign &#x2030; */
+   { 8241, LATEX_MACRO, "textpertenthousand",{ "{\\textpertenthousand}","\\textpertenthousand",  NULL                  } }, /* Per ten thousand sign &#x2031;*/
+   { 8260, LATEX_MACRO, "textfractionssolidus",{"{\\textfractionsolidus}", "\\textfractionsolidus", NULL               } }, /* &x8260; */
+   { 8451, LATEX_MACRO, "textcelcius",       { "{\\textcelcius}",       "\\textcelcius",         NULL                  } }, /* Celcicus &#x2103; */
+   { 8470, LATEX_MACRO, "textnumero",        { "{\\textnumero}",        "\\textnumero",          NULL                  } }, /* Numero symbol &#x2116; */
+   { 8486, LATEX_MACRO, "textohm",           { "{\\textohm}",           "\\textohm",             NULL                  } }, /* Ohm symbol &#x2126; */
+   { 8487, LATEX_MACRO, "textmho",           { "{\\textmho}",           "\\textmho",             NULL                  } }, /* Mho symbol &#x2127; */
+   { 8730, LATEX_MACRO, "textsurd",          { "{\\textsurd}",          "\\textsurd",            NULL                  } }, /* &#x221A; */
 
-   { 185, "{\\textonesuperior}", "\\textonesuperior", "" },   /*Superscript 1 &#xB9; */
-   { 178, "{\\texttwosuperior}", "\\texttwosuperior", "" },   /*Superscript 2 &#xB2; */
-   { 179, "{\\textthreesuperior}", "\\textthreesuperior", "" }, /*Superscript 3 &#xB3; */
+   { 185,  LATEX_MACRO, "textonesuperior",   { "{\\textonesuperior}",   "\\textonesuperior",     "$^1$"                } }, /*Superscript 1 &#xB9; */
+   { 178,  LATEX_MACRO, "texttwosuperior",   { "{\\texttwosuperior}",   "\\texttwosuperior",     "$^2$"                } }, /*Superscript 2 &#xB2; */
+   { 179,  LATEX_MACRO, "textthreesuperior", { "{\\textthreesuperior}", "\\textthreesuperior",   "$^3$"                } }, /*Superscript 3 &#xB3; */
 
-   { 161, "{\\textexclamdown}", "\\textexclamdown", "" },   /* Inverted exclamation mark &#xA1;*/
-   { 191, "{\\textquestiondown}", "\\textquestiondown", "" }, /* Inverted question mark &#xBF; */
+   { 161,  LATEX_MACRO, "textexclamdown",    { "{\\textexclamdown}",    "\\textexclamdown",      NULL                  } }, /* Inverted exclamation mark &#xA1;*/
+   { 191,  LATEX_MACRO, "textquestiondown",  { "{\\textquestiondown}",  "\\textquestiondown",    NULL                  } }, /* Inverted question mark &#xBF; */
 
-   { 162, "{\\textcent}", "\\textcent", "" },         /* Cent sign &#xA2; */
-   { 163, "{\\textsterling}", "\\textsterling", "\\pounds" },     /* Pound sign &#xA3; */
-   { 165, "{\\textyen}", "\\textyen", "" },          /* Yen sign &#xA5; */
-   { 402, "{\\textflorin}", "\\textflorin", "" },       /* Florin sign &#x192; */
-   { 3647, "{\\textbaht}", "\\textbaht", "" },        /* Thai currency &#xE3F; */
-   { 8355, "{\\textfrenchfranc}", "\\textfrenchfranc", "" }, /* French franc &#x20A3; */
-   { 8356, "{\\textlira}", "\\textlira", "" },        /* Lira &#x20A4; */
-   { 8358, "{\\textnaira}", "\\textnaria", "" },       /* Naira &#x20A6; */
-   { 8361, "{\\textwon}", "\\textwon", "" },         /* &#x20A9; */
-   { 8363, "{\\textdong}", "\\textdong", "" },        /* Vietnamese currency &#x20AB; */
-   { 8364, "{\\texteuro}", "\\texteuro", "" },        /* Euro sign */
+   { 162,  LATEX_MACRO, "textcent",          { "{\\textcent}",          "\\textcent",            NULL                  } }, /* Cent sign &#xA2; */
+   { 163,  LATEX_MACRO, "textsterling",      { "{\\textsterling}",      "\\textsterling",        "\\pounds"            } }, /* Pound sign &#xA3; */
+   { 165,  LATEX_MACRO, "textyen",           { "{\\textyen}",           "\\textyen",             NULL                  } }, /* Yen sign &#xA5; */
+   { 402,  LATEX_MACRO, "textflorin",        { "{\\textflorin}",        "\\textflorin",          NULL                  } }, /* Florin sign &#x192; */
+   { 3647, LATEX_MACRO, "textbaht",          { "{\\textbaht}",          "\\textbaht",            NULL                  } }, /* Thai currency &#xE3F; */
+   { 8355, LATEX_MACRO, "textfrenchfranc",   { "{\\textfrenchfranc}",   "\\textfrenchfranc",     NULL                  } }, /* French franc &#x20A3; */
+   { 8356, LATEX_MACRO, "textlira",          { "{\\textlira}",          "\\textlira",            NULL                  } }, /* Lira &#x20A4; */
+   { 8358, LATEX_MACRO, "textnaira",         { "{\\textnaira}",         "\\textnaria",           NULL                  } }, /* Naira &#x20A6; */
+   { 8361, LATEX_MACRO, "textwon",           { "{\\textwon}",           "\\textwon",             NULL                  } }, /* &#x20A9; */
+   { 8363, LATEX_MACRO, "textdong",          { "{\\textdong}",          "\\textdong",            NULL                  } }, /* Vietnamese currency &#x20AB; */
+   { 8364, LATEX_MACRO, "texteuro",          { "{\\texteuro}",          "\\texteuro",            NULL                  } }, /* Euro sign */
 
-   { 169, "{\\textcopyright}", "\\textcopyright", "" },           /* Copyright (C) &#xA9; */
-   { 175, "{\\textregistered}", "\\textregistered", "" },          /* Registered sign (R) &#xAF;*/
-   { 8482, "{\\texttrademark}", "\\texttrademark", "$^{TM}$" },   /* Trademark (TM) &#x2122; */
-   { 8480, "{\\textservicemark}", "\\textservicemark", "$^{SM}$" }, /* Servicemark (SM) &#x2120;*/
-   { 8471, "{\\textcircledP}", "\\textcircledP", "" },           /* Circled P &#2117; */
+   { 169,  LATEX_MACRO, "textcopyright",     { "{\\textcopyright}",     "\\textcopyright",       NULL                  } }, /* Copyright (C) &#xA9; */
+   { 175,  LATEX_MACRO, "textregistered",    { "{\\textregistered}",    "\\textregistered",      NULL                  } }, /* Registered sign (R) &#xAF;*/
+   { 8482, LATEX_MACRO, "texttrademark",     { "{\\texttrademark}",     "\\texttrademark",       "$^{TM}$"             } }, /* Trademark (TM) &#x2122; */
+   { 8480, LATEX_MACRO, "textservicemark",   { "{\\textservicemark}",   "\\textservicemark",     "$^{SM}$"             } }, /* Servicemark (SM) &#x2120;*/
+   { 8471, LATEX_MACRO, "textcircledP",      { "{\\textcircledP}",      "\\textcircledP",        NULL                  } }, /* Circled P &#2117; */
 
 };
 
@@ -505,31 +509,35 @@
 latex2char( char *s, unsigned int *pos, int *unicode )
 {
 	unsigned int value;
-	char *p, *q[3];
-	int i, j, l[3];
+//	char *p, *q[3];
+	int i, j, len;
+	char *p;
+
 	p = &( s[*pos] );
 	value = (unsigned char) *p;
 	if ( value=='{' || value=='\\' || value=='~' || 
 	     value=='$' || value=='\'' || value=='`' || 
 	     value=='-' || value=='^' ) {
-		if ( *p=='\\' && ( *p=='{' || *p=='}' ) ) {
-		} else {
+//		if ( *p=='\\' && ( *p=='{' || *p=='}' ) ) {
+//		} else {
 		for ( i=0; i<nlatex_chars; ++i ) {
-			q[0] = latex_chars[i].bib1;
-			l[0] = strlen( q[0] );
-			q[1] = latex_chars[i].bib2;
-			l[1] = strlen( q[1] );
-			q[2] = latex_chars[i].bib3;
-			l[2] = strlen( q[2] );
+//			q[0] = latex_chars[i].bib1;
+//			l[0] = strlen( q[0] );
+//			q[1] = latex_chars[i].bib2;
+//			l[1] = strlen( q[1] );
+//			q[2] = latex_chars[i].bib3;
+//			l[2] = strlen( q[2] );
 			for ( j=0; j<3; ++j ) {
-				if ( l[j] && !strncmp( p, q[j], l[j] ) ) {
-					*pos = *pos + l[j];
+				if ( latex_chars[i].variant[j] == NULL ) continue;
+				len = strlen( latex_chars[i].variant[j] );
+				if ( !strncmp( p, latex_chars[i].variant[j], len ) ) {
+					*pos = *pos + len;
 					*unicode = 1;
 					return latex_chars[i].unicode;
 				}
 			}
 		}
-		}
+//		}
 	}
 	*unicode = 0;
 	*pos = *pos + 1;
@@ -539,20 +547,51 @@
 void
 uni2latex( unsigned int ch, char buf[], int buf_size )
 {
-	int i;
+	int i, j, n;
+
+	if ( buf_size==0 ) return;
+
 	buf[0] = '?';
 	buf[1] = '\0';
+
 	if ( ch==' ' ) {
 		buf[0] = ' '; /*special case to avoid &nbsp;*/
 		return;
 	}
+
 	for ( i=0; i<nlatex_chars; ++i ) {
 		if ( ch == latex_chars[i].unicode ) {
-			strncpy( buf, latex_chars[i].bib1, buf_size );
-			buf[ buf_size-1 ] = '\0';
+			n = 0;
+
+			if ( latex_chars[i].type == LATEX_MACRO ) {
+				if ( n < buf_size ) buf[n++] = '{';
+				if ( n < buf_size ) buf[n++] = '\\';
+			}
+			else if ( latex_chars[i].type == LATEX_MATH ) {
+				if ( n < buf_size ) buf[n++] = '$';
+				if ( n < buf_size ) buf[n++] = '\\';
+			}
+
+			j = 0;
+			while ( latex_chars[i].out[j] ) {
+				if ( n < buf_size ) buf[n++] = latex_chars[i].out[j];
+				j++;
+			}
+
+			if ( latex_chars[i].type == LATEX_MACRO ) {
+				if ( n < buf_size ) buf[n++] = '}';
+			}
+			else if ( latex_chars[i].type == LATEX_MATH ) {
+				if ( n < buf_size ) buf[n++] = '$';
+			}
+
+			if ( n < buf_size ) buf[n] = '\0';
+			else buf[ buf_size-1 ] = '\0';
+
 			return;
 		}
 	}
-	if ( ch < 128 && buf[0]=='?' ) buf[0] = (char)ch;
+
+	if ( ch < 128 ) buf[0] = (char)ch;
 }
 
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-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/list.c b/bibutils/list.c
deleted file mode 100644
--- a/bibutils/list.c
+++ /dev/null
@@ -1,607 +0,0 @@
-/*
- * list.c
- *
- * version: 2014-11-15
- *
- * Copyright (c) Chris Putnam 2004-2014
- *
- * Source code released under the GPL version 2
- *
- * Implements a simple managed array of newstrs.
- *
- */
-#include "list.h"
-
-void
-list_init( list *a  )
-{
-	a->str = NULL;
-	a->max = 0;
-	a->n = 0;
-	a->sorted = 1;
-}
-
-void
-list_empty( list *a )
-{
-	int i;
-	for ( i=0; i<a->max; ++i )
-		newstr_empty( &(a->str[i]) );
-	a->n = 0;
-	a->sorted = 1;
-}
-
-void
-list_free( list *a )
-{
-	int i;
-	for ( i=0; i<a->max; ++i )
-		newstr_free( &(a->str[i]) );
-	free( a->str );
-	list_init( a );
-}
-
-list *
-list_new( void )
-{
-	list *a = ( list * ) malloc( sizeof ( list ) );
-	if ( a ) list_init( a );
-	return a;
-}
-
-void
-list_delete( list *a )
-{
-	list_free( a );
-	free( a );
-}
-
-/*
- * returns 1 if n is valid string in list
- */
-static inline int
-list_valid_num( list *a, int n )
-{
-	if ( n < 0 || n >= a->n ) return 0;
-	return 1;
-}
-
-static int
-list_comp( const void *v1, const void *v2 )
-{
-	newstr *s1 = ( newstr *) v1;
-	newstr *s2 = ( newstr *) v2;
-	if ( !s1->len && !s2->len ) return 0;
-	else if ( !s1->len ) return -1;
-	else if ( !s2->len ) return 1;
-	else return strcmp( s1->data, s2->data );
-}
-
-static int
-list_comp_step( list *a, int n1, int n2 )
-{
-	return list_comp( (const void*) &(a->str[n1]), (const void*) &(a->str[n2]) );
-}
-
-static newstr *
-list_set_cleanup( list *a, int n )
-{
-	if ( newstr_memerr( &(a->str[n]) ) ) return NULL;
-	if ( a->sorted ) {
-		if ( n>0 && list_comp_step( a, n-1, n )>0 )
-			a->sorted = 0;
-	}
-	if ( a->sorted ) {
-		if ( n<a->n-1 && list_comp_step( a, n, n+1 )>0 )
-			a->sorted = 0;
-	}
-	return &(a->str[n]);
-}
-
-newstr *
-list_set( list *a, int n, newstr *s )
-{
-	if ( !list_valid_num( a, n ) ) return NULL;
-	newstr_newstrcpy( &(a->str[n]), s );
-	return list_set_cleanup( a, n );
-}
-
-newstr *
-list_setc( list *a, int n, const char *s )
-{
-	if ( !list_valid_num( a, n ) ) return NULL;
-	newstr_strcpy( &(a->str[n]), s );
-	return list_set_cleanup( a, n );
-}
-
-/*
- * return pointer to newstr 'n'
- */
-newstr *
-list_get( list *a, int n )
-{
-	if ( !list_valid_num( a, n ) ) return NULL;
-	else return &(a->str[n]);
-}
-
-/*
- * return pointer to C string 'n'
- *
- * So long as the index is a valid number ensure
- * that a pointer is returned even if the newstr doesn't
- * point to data. Only return NULL if the index
- * is invalid. Thus we can convert loops like:
- *
- * for ( i=0; i<a->n; ++i ) {
- *      p = list_getc( a, i );
- *      if ( p==NULL ) continue; // empty string
- *      ...
- * }
- *
- * to
- *
- * i = 0;
- * while ( ( p = list_getc( a, i ) ) ) {
- *      ...
- *      i++;
- * }
- *
- */
-char *
-list_getc( list *a, int n )
-{
-	static char empty[] = "";
-	char *p;
-	if ( !list_valid_num( a, n ) ) return NULL;
-	p = a->str[n].data;
-	if ( p ) return p;
-	else return empty;
-}
-
-static int
-list_alloc( list *a )
-{
-	int i, alloc = 20;
-	a->str = ( newstr* ) malloc( sizeof( newstr ) * alloc );
-	if ( !(a->str) ) return LIST_ERR;
-	a->max = alloc;
-	a->n = 0;
-	for ( i=0; i<alloc; ++i )
-		newstr_init( &(a->str[i]) );
-	return LIST_OK;
-}
-
-static int
-list_realloc( list *a )
-{
-	newstr *more;
-	int i, alloc = a->max * 2;
-	more = ( newstr* ) realloc( a->str, sizeof( newstr ) * alloc );
-	if ( !more ) return LIST_ERR;
-	a->str = more;
-	for ( i=a->max; i<alloc; ++i )
-		newstr_init( &(a->str[i]) );
-	a->max = alloc;
-	return LIST_OK;
-}
-
-
-static int
-list_ensure_space( list *a )
-{
-	int status = LIST_OK;
-	if ( a->max==0 )
-		status = list_alloc( a );
-	else if ( a->n >= a->max )
-		status = list_realloc( a );
-	return status;
-}
-
-newstr *
-list_addvp( list *a, unsigned char mode, void *vp )
-{
-	newstr *s = NULL;
-	int status;
-	status = list_ensure_space( a );
-	if ( status==LIST_OK ) {
-		s = &( a->str[a->n] );
-		if ( mode==LIST_CHR )
-			newstr_strcpy( s, (const char*) vp );
-		else if ( mode==LIST_STR )
-			newstr_newstrcpy( s, (newstr*) vp );
-		else
-			return NULL;
-		if ( newstr_memerr( s ) ) return NULL;
-		a->n++;
-		if ( a->sorted && a->n > 1 ) {
-			if ( list_comp_step( a, a->n-2, a->n-1 ) > 0 )
-				a->sorted = 0;
-		}
-	}
-	return s;
-}
-newstr *
-list_addc( list *a, const char *s )
-{
-	return list_addvp( a, LIST_CHR, (void*)s );
-}
-newstr *
-list_add( list *a, newstr *s )
-{
-	return list_addvp( a, LIST_STR, (void*)s );
-}
-
-newstr *
-list_addvp_unique( list *a, unsigned char mode, void *vp )
-{
-	newstr *s;
-	int n;
-	if ( mode==LIST_CHR )
-		n = list_find( a, (const char*) vp );
-	else if ( mode==LIST_STR )
-		n = list_find( a, ( (newstr*) vp )->data );
-	else
-		return NULL;
-	if ( n!=-1 )
-		s = &( a->str[n] );
-	else {
-		s = list_addvp( a, mode, vp );
-	}
-	return s;
-}
-newstr *
-list_addc_unique( list *a, const char *s )
-{
-	return list_addvp_unique( a, LIST_CHR, (void*)s );
-}
-newstr *
-list_add_unique( list *a, newstr *s )
-{
-	return list_addvp_unique( a, LIST_STR, (void*)s );
-}
-
-int
-list_addvp_all( list *a, unsigned char mode, ... )
-{
-	int ret = LIST_OK;
-	va_list ap;
-	newstr *s;
-	void *v;
-	va_start( ap, mode );
-	do {
-		if ( mode==LIST_CHR ) v = va_arg( ap, char * );
-		else v = va_arg( ap, newstr * );
-		if ( v ) {
-			s = list_addvp( a, mode, v );
-			if ( s==NULL ) { ret = LIST_ERR; goto out; }
-		}
-	} while ( v );
-out:
-	va_end( ap );
-	return ret;
-}
-
-int
-list_add_all( list *a, ... )
-{
-	int ret = LIST_OK;
-	va_list ap;
-	newstr *s, *v;
-	va_start( ap, a );
-	do {
-		v = va_arg( ap, newstr * );
-		if ( v ) {
-			s = list_addvp( a, LIST_STR, (void*)v );
-			if ( s==NULL ) { ret = LIST_ERR; goto out; }
-		}
-	} while ( v );
-out:
-	va_end( ap );
-	return ret;
-}
-
-int
-list_addc_all( list *a, ... )
-{
-	int ret = LIST_OK;
-	va_list ap;
-	newstr *s;
-	const char *v;
-	va_start( ap, a );
-	do {
-		v = va_arg( ap, const char * );
-		if ( v ) {
-			s = list_addvp( a, LIST_CHR, (void*)v );
-			if ( s==NULL ) { ret = LIST_ERR; goto out; }
-		}
-	} while ( v );
-out:
-	va_end( ap );
-	return ret;
-}
-
-int
-list_append( list *a, list *toadd )
-{
-	newstr *s;
-	int i;
-	for ( i=0; i<toadd->n; ++i ) {
-		s = list_add( a, &(toadd->str[i]) );
-		if ( !s ) return LIST_ERR;
-	}
-	return LIST_OK;
-}
-
-int
-list_append_unique( list *a, list *toadd )
-{
-	newstr *s;
-	int i;
-	for ( i=0; i<toadd->n; ++i ) {
-		s = list_add_unique( a, &(toadd->str[i]) );
-		if ( !s ) return LIST_ERR;
-	}
-	return LIST_OK;
-}
-
-int
-list_remove( list *a, int n )
-{
-	int i;
-	if ( !list_valid_num( a, n ) ) return -1;
-	for ( i=n+1; i<a->n; ++i ) {
-		newstr_newstrcpy( &(a->str[i-1]), &(a->str[i]) );
-		if ( newstr_memerr( &(a->str[i-1]) ) ) return LIST_ERR;
-	}
-	a->n--;
-	return LIST_OK;
-}
-
-void
-list_swap( list *a, int n1, int n2 )
-{
-	newstr_swapstrings( &(a->str[n1]), &(a->str[n2]) );
-}
-
-void
-list_sort( list *a )
-{
-	qsort( a->str, a->n, sizeof( newstr ), list_comp );
-	a->sorted = 1;
-}
-
-static int
-list_find_sorted( list *a, const char *searchstr )
-{
-	int min, max, mid, comp;
-	newstr s, *cs;
-	newstr_init( &s );
-	newstr_strcpy( &s, searchstr );
-	min = 0;
-	max = a->n - 1;
-	while ( min <= max ) {
-		mid = ( min + max ) / 2;
-		cs = list_get( a, mid );
-		comp = list_comp( (void*)cs, (void*) (&s) );
-		if ( comp==0 ) {
-			newstr_free( &s );
-			return mid;
-		}
-		else if ( comp > 0 ) max = mid - 1;
-		else if ( comp < 0 ) min = mid + 1;
-	}
-	newstr_free( &s );
-	return -1;
-}
-
-static int
-list_find_simple( list *a, const char *searchstr, int nocase )
-{
-	int i;
-	if ( nocase ) {
-		for ( i=0; i<a->n; ++i )
-			if ( !strcasecmp(a->str[i].data,searchstr) ) 
-				return i;
-	} else {
-		for ( i=0; i<a->n; ++i )
-			if ( !strcmp(a->str[i].data,searchstr) )
-				return i;
-	}
-	return -1;
-}
-
-int
-list_find( list *a, const char *searchstr )
-{
-	if ( a->n==0 ) return -1;
-	if ( a->sorted )
-		return list_find_sorted( a, searchstr );
-	else
-		return list_find_simple( a, searchstr, 0 );
-}
-
-int
-list_findnocase( list *a, const char *searchstr )
-{
-	return list_find_simple( a, searchstr, 1 );
-}
-
-int
-list_fillfp( list *a, FILE *fp, unsigned char skip_blank_lines )
-{
-	int bufpos = 0, ret = LIST_OK;
-	char buf[512]="";
-	newstr line;
-
-	list_empty( a );
-	newstr_init( &line );
-	while ( newstr_fget( fp, buf, sizeof(buf), &bufpos, &line ) ) {
-		if ( skip_blank_lines && line.len==0 ) continue;
-		if ( !list_add( a, &line ) ) { ret = LIST_ERR; goto out; }
-	}
-out:
-	newstr_free( &line );
-	return ret;
-}
-
-int
-list_fill( list *a, const char *filename, unsigned char skip_blank_lines )
-{
-	FILE *fp;
-	int ret;
-
-	fp = fopen( filename, "r" );
-	if ( !fp ) return LIST_ERR_CANNOTOPEN;
-
-	ret = list_fillfp( a, fp, skip_blank_lines );
-
-	fclose( fp );
-
-	return ret;
-}
-
-int
-list_copy( list *to, list *from )
-{
-	int i;
-
-	list_free( to );
-
-	if ( from->n==0 ) return LIST_OK;
-
-	to->str = ( newstr * ) malloc( sizeof( newstr ) * from->n );
-	if ( !to->str ) {
-		to->n = to->max = 0;
-		return LIST_ERR;
-	}
-
-	to->max = from->n;
-	to->sorted = from->sorted;
-
-	for ( i=0; i<from->n; i++ )
-		newstr_init( &(to->str[i]) );
-
-	for ( i=0; i<from->n; i++ ) {
-		newstr_newstrcpy( &(to->str[i]), &(from->str[i]) );
-		if ( newstr_memerr( &(to->str[i]) ) ) return LIST_ERR;
-		to->n += 1;
-	}
-	return LIST_OK;
-}
-
-list *
-list_dup( list *from )
-{
-	list *to;
-	int ok;
-
-	to = list_new();
-	if ( to ) {
-		ok = list_copy( to, from );
-		if ( !ok ) {
-			list_delete( to );
-			to = NULL;
-		}
-	}
-
-	return to;
-}
-
-int
-list_match_entry( list *a, int n, char *s )
-{
-	if ( n < 0 || n >= a->n ) return 0;
-	if ( strcmp( a->str[n].data, s ) ) return 0;
-	return 1;
-}
-
-void
-list_trimend( list *a, int n )
-{
-	int i;
-	if ( a->n - n < 1 ) {
-		list_empty( a );
-	} else {
-		for ( i=a->n -n; i<a->n; ++i ) {
-			newstr_empty( &(a->str[i]) );
-		}
-		a->n -= n;
-	}
-}
-
-int
-list_tokenizec( list *tokens, char *p, const char *delim, int merge_delim )
-{
-	int ret = LIST_OK;
-	newstr s, *t;
-	char *q;
-	list_empty( tokens );
-	newstr_init( &s );
-	while ( p && *p ) {
-		q = p;
-		while ( *q && !strchr( delim, *q ) ) q++;
-		newstr_segcpy( &s, p, q );
-		if ( newstr_memerr( &s ) ) { ret = LIST_ERR; goto out; }
-		if ( s.len ) {
-			t = list_addvp( tokens, LIST_STR, (void*) &s );
-			if ( !t ) { ret = LIST_ERR; goto out; }
-		} else if ( !merge_delim ) {
-			t = list_addvp( tokens, LIST_CHR, (void*) "" );
-			if ( !t ) { ret = LIST_ERR; goto out; }
-		}
-		p = q;
-		if ( *p ) p++;
-	}
-out:
-	newstr_free( &s );
-	return ret;
-}
-
-int
-list_tokenize( list *tokens, newstr *in, const char *delim, int merge_delim )
-{
-	return list_tokenizec( tokens, in->data, delim, merge_delim );
-}
-
-void
-lists_init( list *a, ... )
-{
-	list *a2;
-	va_list ap;
-	list_init( a );
-	va_start( ap, a );
-	do {
-		a2 = va_arg( ap, list * );
-		if ( a2 ) list_init( a2 );
-	} while ( a2 );
-	va_end( ap );
-}
-
-void
-lists_free( list *a, ... )
-{
-	list *a2;
-	va_list ap;
-	list_free( a );
-	va_start( ap, a );
-	do {
-		a2 = va_arg( ap, list * );
-		if ( a2 ) list_free( a2 );
-	} while ( a2 );
-	va_end( ap );
-}
-
-void
-lists_empty( list *a, ... )
-{
-	list *a2;
-	va_list ap;
-	list_empty( a );
-	va_start( ap, a );
-	do {
-		a2 = va_arg( ap, list * );
-		if ( a2 ) list_empty( a2 );
-	} while ( a2 );
-	va_end( ap );
-}
-
diff --git a/bibutils/list.h b/bibutils/list.h
deleted file mode 100644
--- a/bibutils/list.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * list.h
- *
- * version: 2014-11-15
- *
- * Copyright (c) Chris Putnam 2004-2014
- *
- * Source code released under the GPL version 2
- *
- */
-
-#ifndef LISTS_H
-#define LISTS_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include "newstr.h"
-
-#define LIST_ERR (0)
-#define LIST_ERR_CANNOTOPEN (-1)
-#define LIST_OK  (1)
-
-#define LIST_CHR (0)
-#define LIST_STR (1)
-
-typedef struct list {
-	int n, max;
-	newstr *str;
-	unsigned char sorted;
-} list;
-
-
-extern void    lists_init( list *a, ... );
-extern void    lists_free( list *a, ... );
-extern void    lists_empty( list *a, ... );
-
-
-extern void    list_init( list *a );
-extern void    list_free( list *a );
-extern void    list_empty( list *a );
-
-extern list *  list_new( void );
-extern void    list_delete( list * );
-
-extern list*   list_dup( list *a );
-extern int     list_copy( list *to, list *from );
-
-extern newstr * list_addvp( list *a, unsigned char mode, void *vp );
-extern newstr * list_addc( list *a, const char *value );
-extern newstr * list_add( list *a, newstr *value );
-
-extern int      list_addvp_all( list *a, unsigned char mode, ... );
-extern int      list_addc_all( list *a, ... );
-extern int      list_add_all( list *a, ... );
-
-extern newstr * list_addvp_unique( list *a, unsigned char mode, void *vp );
-extern newstr * list_addc_unique( list *a, const char *value );
-extern newstr * list_add_unique( list *a, newstr *value );
-
-extern int     list_append( list *a, list *toadd );
-extern int     list_append_unique( list *a, list *toadd );
-
-extern int     list_remove( list *a, int n );
-
-extern newstr* list_get( list *a, int n );
-extern char*   list_getc( list *a, int n );
-
-extern newstr* list_set( list *a, int n, newstr *s );
-extern newstr* list_setc( list *a, int n, const char *s );
-
-extern void    list_sort( list *a );
-
-extern void    list_swap( list *a, int n1, int n2 );
-
-extern int     list_find( list *a, const char *searchstr );
-extern int     list_findnocase( list *a, const char *searchstr );
-extern int     list_match_entry( list *a, int n, char *s );
-extern void    list_trimend( list *a, int n );
-
-extern int     list_fill( list *a, const char *filename, unsigned char skip_blank_lines );
-extern int     list_fillfp( list *a, FILE *fp, unsigned char skip_blank_lines );
-extern int     list_tokenize( list *tokens, newstr *in, const char *delim, int merge_delim );
-extern int     list_tokenizec( list *tokens, char *p, const char *delim, int merge_delim );
-
-#endif
diff --git a/bibutils/marc.c b/bibutils/marc.c
--- a/bibutils/marc.c
+++ b/bibutils/marc.c
@@ -1,7 +1,7 @@
 /*
  * marc.c
  *
- * Copyright (c) Chris Putnam 2004-2014
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/marc.h b/bibutils/marc.h
--- a/bibutils/marc.h
+++ b/bibutils/marc.h
@@ -1,7 +1,7 @@
 /*
  * marc.h
  *
- * Copyright (c) Chris Putnam 2008-2013
+ * Copyright (c) Chris Putnam 2008-2017
  *
  * 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-2014
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
@@ -9,15 +9,19 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "is_ws.h"
-#include "newstr.h"
-#include "newstr_conv.h"
+#include "str.h"
+#include "str_conv.h"
 #include "fields.h"
 #include "xml.h"
 #include "xml_encoding.h"
-#include "medin.h"
 #include "iso639_2.h"
 #include "bibutils.h"
+#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 );
+
+
 /*****************************************************
  PUBLIC: void medin_initparams()
 *****************************************************/
@@ -44,8 +48,8 @@
 	p->all      = NULL;
 	p->nall     = 0;
 
-	list_init( &(p->asis) );
-	list_init( &(p->corps) );
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
 
 	if ( !progname ) p->progname = NULL;
 	else p->progname = strdup( progname );
@@ -82,14 +86,14 @@
 	return endptr;
 }
 
-int
-medin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset )
+static int
+medin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
-	newstr tmp;
+	str tmp;
 	char *startptr = NULL, *endptr;
 	int haveref = 0, inref = 0, file_charset = CHARSET_UNKNOWN, m, type = -1;
-	newstr_init( &tmp );
-	while ( !haveref && newstr_fget( fp, buf, bufsize, bufpos, line ) ) {
+	str_init( &tmp );
+	while ( !haveref && str_fget( fp, buf, bufsize, bufpos, line ) ) {
 		if ( line->data ) {
 			m = xml_getencoding( line );
 			if ( m!=CHARSET_UNKNOWN ) file_charset = m;
@@ -98,19 +102,19 @@
 			startptr = medin_findstartwrapper( line->data, &type );
 		}
 		if ( startptr || inref ) {
-			if ( inref ) newstr_strcat( &tmp, line->data );
+			if ( inref ) str_strcat( &tmp, line );
 			else {
-				newstr_strcat( &tmp, startptr );
+				str_strcatc( &tmp, startptr );
 				inref = 1;
 			}
-			endptr = medin_findendwrapper( tmp.data, type );
+			endptr = medin_findendwrapper( str_cstr( &tmp ), type );
 			if ( endptr ) {
-				newstr_segcpy( reference, tmp.data, endptr );
+				str_segcpy( reference, str_cstr( &tmp ), endptr );
 				haveref = 1;
 			}
 		}
 	}
-	newstr_free( &tmp );
+	str_free( &tmp );
 	*fcharset = file_charset;
 	return haveref;
 }
@@ -132,8 +136,8 @@
 	int i, fstatus;
 	char *d;
 	*found = 0;
-	if ( !xml_hasdata( node ) ) return BIBL_OK;
-	d = xml_data( node );
+	if ( !xml_hasvalue( node ) ) return BIBL_OK;
+	d = xml_value( node );
 	for ( i=0; i<nc && *found==0; ++i ) {
 		if ( c[i].a==NULL ) {
 			if ( xml_tagexact( node, c[i].in ) ) {
@@ -159,8 +163,8 @@
 medin_articletitle( xml *node, fields *info )
 {
 	int fstatus, status = BIBL_OK;
-	if ( xml_hasdata( node ) ) {
-		fstatus = fields_add( info, "TITLE", xml_data( node ), 0 );
+	if ( xml_hasvalue( node ) ) {
+		fstatus = fields_add( info, "TITLE", xml_value( node ), 0 );
 		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
 	}
 	return status;
@@ -171,33 +175,36 @@
 medin_medlinedate( fields *info, char *p, int level )
 {
 	int fstatus;
-	newstr tmp;
+	str tmp;
 
-	newstr_init( &tmp );
+	str_init( &tmp );
 
-	p = newstr_cpytodelim( &tmp, skip_ws( p ), " \t\n\r", 0 );
-	if ( newstr_memerr( &tmp ) ) return BIBL_ERR_MEMERR;
-	if ( tmp.len > 0 ) {
-		fstatus = fields_add( info, "PARTYEAR", tmp.data, level );
+	p = str_cpytodelim( &tmp, skip_ws( p ), " \t\n\r", 0 );
+	if ( str_memerr( &tmp ) ) return BIBL_ERR_MEMERR;
+
+	if ( str_has_value( &tmp ) ) {
+		fstatus = fields_add( info, "PARTDATE:YEAR", str_cstr( &tmp ), level );
 		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 
-	p = newstr_cpytodelim( &tmp, skip_ws( p ), " \t\n\r", 0 );
-	if ( newstr_memerr( &tmp ) ) return BIBL_ERR_MEMERR;
-	if ( tmp.len > 0 ) {
-		newstr_findreplace( &tmp, "-", "/" );
-		fstatus = fields_add( info, "PARTMONTH", tmp.data, level );
+	p = str_cpytodelim( &tmp, skip_ws( p ), " \t\n\r", 0 );
+	if ( str_memerr( &tmp ) ) return BIBL_ERR_MEMERR;
+
+	if ( str_has_value( &tmp ) ) {
+		str_findreplace( &tmp, "-", "/" );
+		fstatus = fields_add( info, "PARTDATE:MONTH", str_cstr( &tmp ), level );
 		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 
-	p = newstr_cpytodelim( &tmp, skip_ws( p ), " \t\n\r", 0 );
-	if ( newstr_memerr( &tmp ) ) return BIBL_ERR_MEMERR;
-	if ( tmp.len > 0 ) {
-		fstatus = fields_add( info, "PARTDAY", tmp.data, level );
+	p = str_cpytodelim( &tmp, skip_ws( p ), " \t\n\r", 0 );
+	if ( str_memerr( &tmp ) ) return BIBL_ERR_MEMERR;
+
+	if ( str_has_value( &tmp ) ) {
+		fstatus = fields_add( info, "PARTDATE:DAY", str_cstr( &tmp ), level );
 		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 
-	newstr_free( &tmp );
+	str_free( &tmp );
 
 	return BIBL_OK;
 }
@@ -209,7 +216,7 @@
 {
 	char *code, *language;
 	int fstatus;
-	code = xml_data( node );
+	code = xml_value( node );
 	if ( !code ) return BIBL_OK;
 	language = iso639_2_from_code( code );
 	if ( language )
@@ -252,22 +259,22 @@
 medin_journal1( xml *node, fields *info )
 {
 	xml_convert c[] = {
-		{ "Title",           NULL, NULL, "TITLE",      1 },
-		{ "ISOAbbreviation", NULL, NULL, "SHORTTITLE", 1 },
-		{ "ISSN",            NULL, NULL, "ISSN",       1 },
-		{ "Volume",          NULL, NULL, "VOLUME",     1 },
-		{ "Issue",           NULL, NULL, "ISSUE",      1 },
-		{ "Year",            NULL, NULL, "PARTYEAR",   1 },
-		{ "Month",           NULL, NULL, "PARTMONTH",  1 },
-		{ "Day",             NULL, NULL, "PARTDAY",    1 },
+		{ "Title",           NULL, NULL, "TITLE",          1 },
+		{ "ISOAbbreviation", NULL, NULL, "SHORTTITLE",     1 },
+		{ "ISSN",            NULL, NULL, "ISSN",           1 },
+		{ "Volume",          NULL, NULL, "VOLUME",         1 },
+		{ "Issue",           NULL, NULL, "ISSUE",          1 },
+		{ "Year",            NULL, NULL, "PARTDATE:YEAR",  1 },
+		{ "Month",           NULL, NULL, "PARTDATE:MONTH", 1 },
+		{ "Day",             NULL, NULL, "PARTDATE:DAY",   1 },
 	};
 	int nc = sizeof( c ) / sizeof( c[0] ), status, found;
-	if ( xml_hasdata( node ) ) {
+	if ( xml_hasvalue( node ) ) {
 		status = medin_doconvert( node, info, c, nc, &found );
 		if ( status!=BIBL_OK ) return status;
 		if ( !found ) {
 			if ( xml_tagexact( node, "MedlineDate" ) ) {
-				status = medin_medlinedate( info, xml_data( node ), 1 );
+				status = medin_medlinedate( info, xml_value( node ), 1 );
 				if ( status!=BIBL_OK ) return status;
 			}
 			if ( xml_tagexact( node, "Language" ) ) {
@@ -295,28 +302,28 @@
 medin_pagination( xml *node, fields *info )
 {
 	int i, fstatus, status;
-	newstr sp, ep;
+	str sp, ep;
 	char *p, *pp;
 	if ( xml_tagexact( node, "MedlinePgn" ) && node->value ) {
-		newstrs_init( &sp, &ep, NULL );
-		p = newstr_cpytodelim( &sp, xml_data( node ), "-", 1 );
-		if ( newstr_memerr( &sp ) ) return BIBL_ERR_MEMERR;
-		if ( sp.len ) {
-			fstatus = fields_add( info, "PAGESTART", sp.data, 1 );
+		strs_init( &sp, &ep, NULL );
+		p = str_cpytodelim( &sp, xml_value( node ), "-", 1 );
+		if ( str_memerr( &sp ) ) return BIBL_ERR_MEMERR;
+		if ( str_has_value( &sp ) ) {
+			fstatus = fields_add( info, "PAGES:START", str_cstr( &sp ), 1 );
 			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 		}
-		p = newstr_cpytodelim( &ep, p, "", 0 );
-		if ( newstr_memerr( &ep ) ) return BIBL_ERR_MEMERR;
-		if ( ep.len ) {
+		p = str_cpytodelim( &ep, p, "", 0 );
+		if ( str_memerr( &ep ) ) return BIBL_ERR_MEMERR;
+		if ( str_has_value( &ep ) ) {
 			if ( sp.len > ep.len ) {
 				for ( i=sp.len-ep.len; i<sp.len; ++i )
 					sp.data[i] = ep.data[i-sp.len+ep.len];
 				pp = sp.data;
 			} else  pp = ep.data;
-			fstatus = fields_add( info, "PAGEEND", pp, 1 );
+			fstatus = fields_add( info, "PAGES:STOP", pp, 1 );
 			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 		}
-		newstrs_free( &sp, &ep, NULL );
+		strs_free( &sp, &ep, NULL );
 	}
 	if ( node->down ) {
 		status = medin_pagination( node->down, info );
@@ -337,8 +344,8 @@
 medin_abstract( xml *node, fields *info )
 {
 	int fstatus;
-	if ( xml_tagwithdata( node, "AbstractText" ) ) {
-		fstatus = fields_add( info, "ABSTRACT", xml_data( node ), 0 );
+	if ( xml_tagwithvalue( node, "AbstractText" ) ) {
+		fstatus = fields_add( info, "ABSTRACT", xml_value( node ), 0 );
 		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	} else if ( node->next ) return medin_abstract( node->next, info );
 	return BIBL_OK;
@@ -357,28 +364,28 @@
  * </AuthorList>
  */
 static int
-medin_author( xml *node, newstr *name )
+medin_author( xml *node, str *name )
 {
 	char *p;
 	if ( xml_tagexact( node, "LastName" ) ) {
-		if ( name->len ) {
-			newstr_prepend( name, "|" );
-			newstr_prepend( name, xml_data( node ) );
+		if ( str_has_value( name ) ) {
+			str_prepend( name, "|" );
+			str_prepend( name, xml_value( node ) );
 		}
-		else newstr_strcat( name, xml_data( node ) );
+		else str_strcatc( name, xml_value( node ) );
 	} else if ( xml_tagexact( node, "ForeName" ) || 
 	            xml_tagexact( node, "FirstName" ) ) {
-		p = xml_data( node );
+		p = xml_value( node );
 		while ( p && *p ) {
-			if ( name->len ) newstr_addchar( name, '|' );
+			if ( str_has_value( name ) ) str_addchar( name, '|' );
 			while ( *p && *p==' ' ) p++;
-			while ( *p && *p!=' ' ) newstr_addchar( name, *p++ );
+			while ( *p && *p!=' ' ) str_addchar( name, *p++ );
 		}
 	} else if ( xml_tagexact( node, "Initials" ) && !strchr( name->data, '|' )) {
-		p = xml_data( node );
+		p = xml_value( node );
 		while ( p && *p ) {
-			if ( name->len ) newstr_addchar( name, '|' );
-			if ( !is_ws(*p) ) newstr_addchar( name, *p++ );
+			if ( str_has_value( name ) ) str_addchar( name, '|' );
+			if ( !is_ws(*p) ) str_addchar( name, *p++ );
 		}
 	}
 	if ( node->next ) medin_author( node->next, name );
@@ -386,10 +393,10 @@
 }
 
 static int
-medin_corpauthor( xml *node, newstr *name )
+medin_corpauthor( xml *node, str *name )
 {
 	if ( xml_tagexact( node, "CollectiveName" ) ) {
-		newstr_strcpy( name, xml_data( node ) );
+		str_strcpyc( name, xml_value( node ) );
 	} else if ( node->next ) medin_corpauthor( node->next, name );
 	return BIBL_OK;
 }
@@ -398,28 +405,28 @@
 medin_authorlist( xml *node, fields *info )
 {
 	int fstatus, status;
-	newstr name;
+	str name;
 	char *tag;
-	newstr_init( &name );
+	str_init( &name );
 	node = node->down;
 	while ( node ) {
 		if ( xml_tagexact( node, "Author" ) && node->down ) {
 			status = medin_author( node->down, &name );
 			tag = "AUTHOR";
-			if ( !name.len ) {
+			if ( str_is_empty( &name ) ) {
 				status = medin_corpauthor( node->down, &name );
 				tag = "AUTHOR:CORP";
 			}
-			if ( newstr_memerr( &name ) || status!=BIBL_OK ) return BIBL_ERR_MEMERR;
-			if ( name.len ) {
+			if ( str_memerr( &name ) || status!=BIBL_OK ) return BIBL_ERR_MEMERR;
+			if ( str_has_value( &name ) ) {
 				fstatus = fields_add(info,tag,name.data,0);
 				if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 			}
-			newstr_empty( &name );
+			str_empty( &name );
 		}
 		node = node->next;
 	}
-	newstr_free( &name );
+	str_free( &name );
 	return BIBL_OK;
 }
 
@@ -439,8 +446,8 @@
 medin_journal2( xml *node, fields *info )
 {
 	int fstatus, status = BIBL_OK;
-	if ( xml_tagwithdata( node, "MedlineTA" ) && fields_find( info, "TITLE", 1 )==-1 ) {
-		fstatus = fields_add( info, "TITLE", xml_data( node ), 1 );
+	if ( xml_tagwithvalue( node, "MedlineTA" ) && fields_find( info, "TITLE", 1 )==-1 ) {
+		fstatus = fields_add( info, "TITLE", xml_value( node ), 1 );
 		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	if ( node->down ) {
@@ -465,8 +472,8 @@
 medin_meshheading( xml *node, fields *info )
 {
 	int fstatus, status = BIBL_OK;
-	if ( xml_tagwithdata( node, "DescriptorName" ) ) {
-		fstatus = fields_add( info, "KEYWORD", xml_data( node ), 0 );
+	if ( xml_tagwithvalue( node, "DescriptorName" ) ) {
+		fstatus = fields_add( info, "KEYWORD", xml_value( node ), 0 );
 		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	if ( node->next ) status = medin_meshheading( node->next, info );
@@ -491,7 +498,7 @@
  *         <ArticleId IdType="pubmed">14523232</ArticleId>
  *         <ArticleId IdType="doi">10.1073/pnas.2133463100</ArticleId>
  *         <ArticleId IdType="pii">2133463100</ArticleId>
- *         <ArticleId IdType="medline">22922082</ArticleId>
+ *         <ArticleId IdType="pmc">PMC4833866</ArticleId>
  *     </ArticleIdList>
  * </PubmedData>
  *
@@ -504,6 +511,7 @@
 		{ "ArticleId", "IdType", "doi",     "DOI",     0 },
 		{ "ArticleId", "IdType", "pubmed",  "PMID",    0 },
 		{ "ArticleId", "IdType", "medline", "MEDLINE", 0 },
+		{ "ArticleId", "IdType", "pmc",     "PMC",     0 },
 		{ "ArticleId", "IdType", "pii",     "PII",     0 },
 	};
 	int nc = sizeof( c ) / sizeof( c[0] ), found, status;
@@ -537,12 +545,12 @@
 	else if ( xml_tagexact( node, "Language" ) )
 		status = medin_language( node, info, 0 );
 	else if ( xml_tagexact( node, "Affiliation" ) ) {
-		fstatus = fields_add( info, "ADDRESS", xml_data( node ), 0 );
+		fstatus = fields_add( info, "ADDRESS", xml_value( node ), 0 );
 		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
 	}
 	if ( status!=BIBL_OK ) return status;
 	if ( node->next ) status = medin_article( node->next, info );
-	return BIBL_OK;
+	return status;
 }
 
 static int
@@ -616,8 +624,8 @@
 	return status;
 }
 
-int
-medin_processf( fields *medin, char *data, char *filename, long nref )
+static int
+medin_processf( fields *medin, char *data, char *filename, long nref, param *p )
 {
 	int status;
 	xml top;
diff --git a/bibutils/medin.h b/bibutils/medin.h
deleted file mode 100644
--- a/bibutils/medin.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * medin.h
- *
- * Copyright (c) Chris Putnam 2004-2014
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef MEDIN_H
-#define MEDIN_H
-
-#include "newstr.h"
-#include "fields.h"
-#include "reftypes.h"
-#include "bibutils.h"
-
-extern int medin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset );
-extern int medin_processf( fields *medin, char *data, char *filename, long nref );
-
-extern void medin_initparams( param *p, const char *progname );
-
-extern variants med_all[];
-extern int med_nall;
-
-#endif
-
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-2014
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
@@ -11,8 +11,8 @@
 #include <string.h>
 #include <ctype.h>
 #include "is_ws.h"
-#include "newstr.h"
-#include "newstr_conv.h"
+#include "str.h"
+#include "str_conv.h"
 #include "xml.h"
 #include "xml_encoding.h"
 #include "fields.h"
@@ -20,12 +20,16 @@
 #include "reftypes.h"
 #include "modstypes.h"
 #include "marc.h"
+#include "url.h"
 #include "iso639_1.h"
 #include "iso639_2.h"
 #include "iso639_3.h"
 #include "bibutils.h"
-#include "modsin.h"
+#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 );
+
 /*****************************************************
  PUBLIC: void modsin_initparams()
 *****************************************************/
@@ -55,8 +59,8 @@
 	p->all      = NULL;
 	p->nall     = 0;
 
-	list_init( &(p->asis) );
-	list_init( &(p->corps) );
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
 
 	if ( !progname ) p->progname = NULL;
 	else p->progname = strdup( progname );
@@ -69,13 +73,13 @@
 static char modsns[]="mods";
 
 static int
-modsin_detailr( xml *node, newstr *value )
+modsin_detailr( xml *node, str *value )
 {
 	int status = BIBL_OK;
 	if ( node->value && node->value->len ) {
-		if ( value->len ) newstr_addchar( value, ' ' );
-		newstr_newstrcat( value, node->value );
-		if ( newstr_memerr( value ) ) return BIBL_ERR_MEMERR;
+		if ( value->len ) str_addchar( value, ' ' );
+		str_strcat( value, node->value );
+		if ( str_memerr( value ) ) return BIBL_ERR_MEMERR;
 	}
 	if ( node->down ) {
 		status = modsin_detailr( node->down, value );
@@ -89,26 +93,26 @@
 static int
 modsin_detail( xml *node, fields *info, int level )
 {
-	newstr type, value, *tp;
+	str type, value, *tp;
 	int fstatus, status = BIBL_OK;
 	if ( node->down ) {
-		newstrs_init( &type, &value, NULL );
+		strs_init( &type, &value, NULL );
 		tp = xml_getattrib( node, "type" );
 		if ( tp ) {
-			newstr_newstrcpy( &type, tp );
-			newstr_toupper( &type );
-			if ( newstr_memerr( &type ) ) goto out;
+			str_strcpy( &type, tp );
+			str_toupper( &type );
+			if ( str_memerr( &type ) ) goto out;
 		}
 		status = modsin_detailr( node->down, &value );
 		if ( status!=BIBL_OK ) goto out;
 		if ( type.data && !strcasecmp( type.data, "PAGE" ) ) {
-			fstatus = fields_add( info, "PAGESTART", value.data, level );
+			fstatus = fields_add( info, "PAGES:START", value.data, level );
 		} else {
 			fstatus = fields_add( info, type.data, value.data, level );
 		}
 		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
 out:
-		newstrs_free( &type, &value, NULL );
+		strs_free( &type, &value, NULL );
 	}
 	return status;
 }
@@ -118,56 +122,56 @@
 {
 	int fstatus, status = BIBL_OK;
 	char *tag, *p = NULL;
-	newstr s;
+	str s;
 	if ( node->value ) p = node->value->data;
 	if ( p ) {
-		newstr_init( &s );
+		str_init( &s );
 
-		p = newstr_cpytodelim( &s, skip_ws( p ), "-", 1 );
-		if ( newstr_memerr( &s ) ) { status = BIBL_ERR_MEMERR; goto out; }
-		if ( s.len ) {
-			tag = ( part ) ? "PARTYEAR" : "YEAR";
-			fstatus =  fields_add( info, tag, s.data, level );
+		p = str_cpytodelim( &s, skip_ws( p ), "-", 1 );
+		if ( str_memerr( &s ) ) { status = BIBL_ERR_MEMERR; goto out; }
+		if ( str_has_value( &s ) ) {
+			tag = ( part ) ? "PARTDATE:YEAR" : "DATE:YEAR";
+			fstatus =  fields_add( info, tag, str_cstr( &s ), level );
 			if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 		}
 
-		p = newstr_cpytodelim( &s, skip_ws( p ), "-", 1 );
-		if ( newstr_memerr( &s ) ) { status = BIBL_ERR_MEMERR; goto out; }
-		if ( s.len ) {
-			tag = ( part ) ? "PARTMONTH" : "MONTH";
-			fstatus =  fields_add( info, tag, s.data, level );
+		p = str_cpytodelim( &s, skip_ws( p ), "-", 1 );
+		if ( str_memerr( &s ) ) { status = BIBL_ERR_MEMERR; goto out; }
+		if ( str_has_value( &s ) ) {
+			tag = ( part ) ? "PARTDATE:MONTH" : "DATE:MONTH";
+			fstatus =  fields_add( info, tag, str_cstr( &s ), level );
 			if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 		}
 
-		p = newstr_cpytodelim( &s, skip_ws( p ), "", 0 );
-		if ( newstr_memerr( &s ) ) { status = BIBL_ERR_MEMERR; goto out; }
-		if ( s.len ) {
-			tag = ( part ) ? "PARTDAY" : "DAY";
-			fstatus =  fields_add( info, tag, s.data, level );
+		p = str_cpytodelim( &s, skip_ws( p ), "", 0 );
+		if ( str_memerr( &s ) ) { status = BIBL_ERR_MEMERR; goto out; }
+		if ( str_has_value( &s ) ) {
+			tag = ( part ) ? "PARTDATE:DAY" : "DATE:DAY";
+			fstatus =  fields_add( info, tag, str_cstr( &s ), level );
 			if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 		}
 out:
-		newstr_free( &s );
+		str_free( &s );
 	}
 	return status;
 }
 
 static int
-modsin_pager( xml *node, newstr *sp, newstr *ep, newstr *tp, newstr *lp )
+modsin_pager( xml *node, str *sp, str *ep, str *tp, str *lp )
 {
 	int status = BIBL_OK;
 	if ( xml_tagexact( node, "start" ) ) {
-		newstr_newstrcpy( sp, node->value );
-		if ( newstr_memerr( sp ) ) return BIBL_ERR_MEMERR;
+		str_strcpy( sp, node->value );
+		if ( str_memerr( sp ) ) return BIBL_ERR_MEMERR;
 	} else if ( xml_tagexact( node, "end" ) ) {
-		newstr_newstrcpy( ep, node->value );
-		if ( newstr_memerr( ep ) ) return BIBL_ERR_MEMERR;
+		str_strcpy( ep, node->value );
+		if ( str_memerr( ep ) ) return BIBL_ERR_MEMERR;
 	} else if ( xml_tagexact( node, "total" ) ) {
-		newstr_newstrcpy( tp, node->value );
-		if ( newstr_memerr( tp ) ) return BIBL_ERR_MEMERR;
+		str_strcpy( tp, node->value );
+		if ( str_memerr( tp ) ) return BIBL_ERR_MEMERR;
 	} else if ( xml_tagexact( node, "list" ) ) {
-		newstr_newstrcpy( lp, node->value );
-		if ( newstr_memerr( lp ) ) return BIBL_ERR_MEMERR;
+		str_strcpy( lp, node->value );
+		if ( str_memerr( lp ) ) return BIBL_ERR_MEMERR;
 	}
 	if ( node->down ) {
 		status = modsin_pager( node->down, sp, ep, tp, lp );
@@ -182,53 +186,49 @@
 modsin_page( xml *node, fields *info, int level )
 {
 	int fstatus, status = BIBL_OK;
-	newstr sp, ep, tp, lp;
+	str sp, ep, tp, lp;
 	xml *dnode = node->down;
 
 	if ( !dnode ) return BIBL_OK;
 
-	newstrs_init( &sp, &ep, &tp, &lp, NULL );
+	strs_init( &sp, &ep, &tp, &lp, NULL );
 
 	status = modsin_pager( dnode, &sp, &ep, &tp, &lp );
 	if ( status!=BIBL_OK ) goto out;
 
-	if ( sp.len || ep.len ) {
-		if ( sp.len ) {
-			fstatus = fields_add( info, "PAGESTART", sp.data, level );
+	if ( str_has_value( &sp ) || str_has_value( &ep ) ) {
+		if ( str_has_value( &sp ) ) {
+			fstatus = fields_add( info, "PAGES:START", str_cstr( &sp ), level );
 			if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 		}
-		if ( ep.len ) {
-			fstatus = fields_add( info, "PAGEEND", ep.data, level );
+		if ( str_has_value( &ep ) ) {
+			fstatus = fields_add( info, "PAGES:STOP", str_cstr( &ep ), level );
 			if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 		}
-	} else if ( lp.len ) {
-		fstatus = fields_add( info, "PAGESTART", lp.data, level );
+	} else if ( str_has_value( &lp ) ) {
+		fstatus = fields_add( info, "PAGES:START", str_cstr( &lp ), level );
 		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 	}
-	if ( tp.len ) {
-		fstatus = fields_add( info, "TOTALPAGES", tp.data, level );
+	if ( str_has_value( &tp ) ) {
+		fstatus = fields_add( info, "PAGES:TOTAL", str_cstr( &tp ), level );
 		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 	}
 out:
-	newstrs_free( &sp, &ep, &tp, &lp, NULL );
-	return BIBL_OK;
+	strs_free( &sp, &ep, &tp, &lp, NULL );
+	return status;
 }
 
 static int
-modsin_titler( xml *node, newstr *title, newstr *subtitle )
+modsin_titler( xml *node, str *title, str *subtitle )
 {
 	int status = BIBL_OK;
 	if ( xml_tagexact( node, "title" ) ) {
-		if ( title->len ) {
-			newstr_strcat( title, " : " );
-			newstr_newstrcat( title, node->value );
-		} else {
-			newstr_newstrcat( title, node->value );
-		}
-		if ( newstr_memerr( title ) ) return BIBL_ERR_MEMERR;
+		if ( str_has_value( title ) ) str_strcatc( title, " : " );
+		str_strcat( title, node->value );
+		if ( str_memerr( title ) ) return BIBL_ERR_MEMERR;
 	} else if ( xml_tagexact( node, "subTitle" ) ) {
-		newstr_newstrcat( subtitle, node->value );
-		if ( newstr_memerr( subtitle ) ) return BIBL_ERR_MEMERR;
+		str_strcat( subtitle, node->value );
+		if ( str_memerr( subtitle ) ) return BIBL_ERR_MEMERR;
 	}
 	if ( node->down ) {
 		status = modsin_titler( node->down, title, subtitle );
@@ -247,31 +247,31 @@
 		{ "SUBTITLE", "SHORTSUBTITLE" },
 	};
 	int fstatus, status = BIBL_OK;
-	newstr title, subtitle;
-	xml *dnode = node->down;
+	str title, subtitle;
+	xml *dnode;
 	int abbr;
 
 	dnode = node->down;
 	if ( !dnode ) return status;
 
-	newstrs_init( &title, &subtitle, NULL );
+	strs_init( &title, &subtitle, NULL );
 	abbr = xml_tag_attrib( node, "titleInfo", "type", "abbreviated" );
 
 	status = modsin_titler( dnode, &title, &subtitle );
 	if ( status!=BIBL_OK ) goto out;
 
-	if ( title.len ) {
-		fstatus = fields_add( info, titletag[0][abbr], title.data, level );
+	if ( str_has_value( &title ) ) {
+		fstatus = fields_add( info, titletag[0][abbr], str_cstr( &title ), level );
 		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 	}
 
-	if ( subtitle.len ) {
-		fstatus = fields_add( info, titletag[1][abbr], subtitle.data, level );
+	if ( str_has_value( &subtitle ) ) {
+		fstatus = fields_add( info, titletag[1][abbr], str_cstr( &subtitle ), level );
 		if ( fstatus!=FIELDS_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 	}
 
 out:
-	newstrs_free( &title, &subtitle, NULL );
+	strs_free( &title, &subtitle, NULL );
 	return status;
 }
 
@@ -284,24 +284,24 @@
  * "author" or "author|creator" or "edt" or "editor|edt".
  */
 static int
-modsin_marcrole_convert( newstr *s, char *suffix, newstr *out )
+modsin_marcrole_convert( str *s, char *suffix, str *out )
 {
 	convert roles[] = {
-		{ "author",              "AUTHOR" },
-		{ "aut",                 "AUTHOR" },
-		{ "aud",                 "AUTHOR" },
-		{ "aui",                 "AUTHOR" },
-		{ "aus",                 "AUTHOR" },
-		{ "creator",             "AUTHOR" },
-		{ "cre",                 "AUTHOR" },
-		{ "editor",              "EDITOR" },
-		{ "edt",                 "EDITOR" },
-		{ "degree grantor",      "DEGREEGRANTOR" },
-		{ "dgg",                 "DEGREEGRANTOR" },
-		{ "organizer of meeting","ORGANIZER" },
-		{ "orm",                 "ORGANIZER" },
-		{ "patent holder",       "ASSIGNEE" },
-		{ "pth",                 "ASSIGNEE" }
+		{ "author",              "AUTHOR",        0, 0 },
+		{ "aut",                 "AUTHOR",        0, 0 },
+		{ "aud",                 "AUTHOR",        0, 0 },
+		{ "aui",                 "AUTHOR",        0, 0 },
+		{ "aus",                 "AUTHOR",        0, 0 },
+		{ "creator",             "AUTHOR",        0, 0 },
+		{ "cre",                 "AUTHOR",        0, 0 },
+		{ "editor",              "EDITOR",        0, 0 },
+		{ "edt",                 "EDITOR",        0, 0 },
+		{ "degree grantor",      "DEGREEGRANTOR", 0, 0 },
+		{ "dgg",                 "DEGREEGRANTOR", 0, 0 },
+		{ "organizer of meeting","ORGANIZER",     0, 0 },
+		{ "orm",                 "ORGANIZER",     0, 0 },
+		{ "patent holder",       "ASSIGNEE",      0, 0 },
+		{ "pth",                 "ASSIGNEE",      0, 0 }
 	};
 	int nroles = sizeof( roles ) / sizeof( roles[0] );
 	int i, nmismatch, n = -1, status = BIBL_OK;
@@ -330,26 +330,26 @@
 	}
 
 	if ( n!=-1 ) {
-		newstr_strcpy( out, roles[n].internal );
-		if ( suffix ) newstr_strcat( out, suffix );
+		str_strcpyc( out, roles[n].internal );
+		if ( suffix ) str_strcatc( out, suffix );
 	} else {
-		newstr_strcpy( out, s->data );
+		str_strcpy( out, s );
 	}
-	if ( newstr_memerr( out ) ) status = BIBL_ERR_MEMERR;
+	if ( str_memerr( out ) ) status = BIBL_ERR_MEMERR;
 	return status;
 }
 
 static int
-modsin_asis_corp_r( xml *node, newstr *name, newstr *role )
+modsin_asis_corp_r( xml *node, str *name, str *role )
 {
 	int status = BIBL_OK;
 	if ( xml_tagexact( node, "namePart" ) ) {
-		newstr_newstrcpy( name, node->value );
-		if ( newstr_memerr( name ) ) return BIBL_ERR_MEMERR;
+		str_strcpy( name, node->value );
+		if ( str_memerr( name ) ) return BIBL_ERR_MEMERR;
 	} else if ( xml_tagexact( node, "roleTerm" ) ) {
-		if ( role->len ) newstr_addchar( role, '|' );
-		newstr_newstrcat( role, node->value );
-		if ( newstr_memerr( role ) ) return BIBL_ERR_MEMERR;
+		if ( role->len ) str_addchar( role, '|' );
+		str_strcat( role, node->value );
+		if ( str_memerr( role ) ) return BIBL_ERR_MEMERR;
 	}
 	if ( node->down ) {
 		status = modsin_asis_corp_r( node->down, name, role );
@@ -361,84 +361,141 @@
 }
 
 static int
-modsin_personr( xml *node, newstr *name, newstr *suffix, newstr *roles )
-{
-	newstr outname;
-	int status = BIBL_OK;
-	newstr_init( &outname );
-	if ( xml_tagexact( node, "namePart" ) ) {
-		if ( xml_tag_attrib( node, "namePart", "type", "family" ) ) {
-			if ( name->len ) newstr_prepend( name, "|" );
-			newstr_prepend( name, node->value->data );
-		} else if (xml_tag_attrib( node, "namePart", "type", "suffix") ||
-		           xml_tag_attrib( node, "namePart", "type", "termsOfAddress" )) {
-			if ( suffix->len ) newstr_addchar( suffix, ' ' );
-			newstr_strcat( suffix, node->value->data );
-		} else if (xml_tag_attrib( node, "namePart", "type", "date")){
-		} else {
-			if ( name->len ) newstr_addchar( name, '|' );
-			name_parse( &outname, node->value, NULL, NULL );
-			newstr_newstrcat( name, &outname );
-		}
-	} else if ( xml_tagexact( node, "roleTerm" ) ) {
-		if ( roles->len ) newstr_addchar( roles, '|' );
-		newstr_newstrcat( roles, node->value );
-	}
-	if ( node->down ) {
-		status = modsin_personr( node->down, name, suffix, roles );
-		if ( status!=BIBL_OK ) goto out;
-	}
-	if ( node->next ) status = modsin_personr( node->next, name, suffix, roles );
-out:
-	newstr_free( &outname );
-	return status;
-}
-
-static int
 modsin_asis_corp( xml *node, fields *info, int level, char *suffix )
 {
 	int fstatus, status = BIBL_OK;
-	newstr name, roles, role_out;
+	str name, roles, role_out;
 	xml *dnode = node->down;
 	if ( dnode ) {
-		newstrs_init( &name, &roles, &role_out, NULL );
+		strs_init( &name, &roles, &role_out, NULL );
 		status = modsin_asis_corp_r( dnode, &name, &roles );
 		if ( status!=BIBL_OK ) goto out;
 		status = modsin_marcrole_convert( &roles, suffix, &role_out );
 		if ( status!=BIBL_OK ) goto out;
-		fstatus = fields_add( info, role_out.data, name.data, level );
+		fstatus = fields_add( info, str_cstr( &role_out ), str_cstr( &name ), level );
 		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
 out:
-		newstrs_free( &name, &roles, &role_out, NULL );
+		strs_free( &name, &roles, &role_out, NULL );
 	}
 	return status;
 }
 
 static int
+modsin_roler( xml *node, str *roles )
+{
+	if ( roles->len ) str_addchar( roles, '|' );
+	str_strcat( roles, node->value );
+	if ( str_memerr( roles ) ) return BIBL_ERR_MEMERR;
+	else return BIBL_OK;
+}
+
+static int
+modsin_personr( xml *node, str *familyname, str *givenname, str *suffix )
+{
+	int status = BIBL_OK;
+
+	if ( xml_tag_attrib( node, "namePart", "type", "family" ) ) {
+		if ( str_has_value( familyname ) ) str_addchar( familyname, ' ' );
+		str_strcat( familyname, node->value );
+		if ( str_memerr( familyname ) ) status = BIBL_ERR_MEMERR;
+	}
+
+	else if ( xml_tag_attrib( node, "namePart", "type", "suffix") ||
+	          xml_tag_attrib( node, "namePart", "type", "termsOfAddress" )) {
+		if ( str_has_value( suffix ) ) str_addchar( suffix, ' ' );
+		str_strcat( suffix, node->value );
+		if ( str_memerr( suffix ) ) status = BIBL_ERR_MEMERR;
+	}
+
+	else if (xml_tag_attrib( node, "namePart", "type", "date") ){
+		/* no nothing */
+	}
+
+	else {
+		if ( str_has_value( givenname ) ) str_addchar( givenname, '|' );
+		str_strcat( givenname, node->value );
+		if ( str_memerr( givenname ) ) status = BIBL_ERR_MEMERR;
+	}
+
+	return status;
+}
+
+static int
 modsin_person( xml *node, fields *info, int level )
 {
-	newstr name, suffix, roles, role_out;
+	str familyname, givenname, name, suffix, roles, role_out;
 	int fstatus, status = BIBL_OK;
-	xml *dnode = node->down;
-	if ( dnode ) {
-		newstrs_init( &name, &suffix, &roles, &role_out, NULL );
+	xml *dnode, *rnode;
 
-		status = modsin_personr( dnode, &name, &suffix, &roles );
-		if ( status!=BIBL_OK ) goto out;
-		if ( suffix.len ) {
-			newstr_strcat( &name, "||" );
-			newstr_newstrcat( &name, &suffix );
-			if ( newstr_memerr( &name ) ) { status=BIBL_ERR_MEMERR; goto out; }
+	dnode = node->down;
+	if ( !dnode ) return status;
+
+	strs_init( &name, &familyname, &givenname, &suffix, &roles, &role_out, NULL );
+
+	while ( dnode ) {
+
+		if ( xml_tagexact( dnode, "namePart" ) ) {
+			status = modsin_personr( dnode, &familyname, &givenname, &suffix );
+			if ( status!=BIBL_OK ) goto out;
 		}
 
-		status = modsin_marcrole_convert( &roles, NULL, &role_out );
-		if ( status!=BIBL_OK ) goto out;
+		else if ( xml_tagexact( dnode, "role" ) ) {
+			rnode = dnode->down;
+			while ( rnode ) {
+				if ( xml_tagexact( rnode, "roleTerm" ) ) {
+					status = modsin_roler( rnode, &roles );
+					if ( status!=BIBL_OK ) goto out;
+				}
+				rnode = rnode->next;
+			}
+		}
 
-		fstatus = fields_add( info, role_out.data, name.data, level );
-		if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
-out:
-		newstrs_free( &name, &suffix, &roles, &role_out, NULL );
+		dnode = dnode->next;
+
 	}
+
+	/*
+	 * Handle:
+	 *          <namePart type='given'>Noah A.</namePart>
+	 *          <namePart type='family'>Smith</namePart>
+	 * without mangling the order of "Noah A."
+	 */
+	if ( str_has_value( &familyname ) ) {
+		str_strcpy( &name, &familyname );
+		if ( givenname.len ) {
+			str_addchar( &name, '|' );
+			str_strcat( &name, &givenname );
+		}
+	}
+
+	/*
+	 * Handle:
+	 *          <namePart>Noah A. Smith</namePart>
+	 * with name order mangling.
+	 */
+	else {
+		if ( str_has_value( &givenname ) )
+			name_parse( &name, &givenname, NULL, NULL );
+	}
+
+	if ( str_has_value( &suffix ) ) {
+		str_strcatc( &name, "||" );
+		str_strcat( &name, &suffix );
+	}
+
+	if ( str_memerr( &name ) ) {
+		status=BIBL_ERR_MEMERR;
+		goto out;
+	}
+
+	status = modsin_marcrole_convert( &roles, NULL, &role_out );
+	if ( status!=BIBL_OK ) goto out;
+
+	fstatus = fields_add_can_dup( info, str_cstr( &role_out ), str_cstr( &name ), level );
+	if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+
+out:
+	strs_free( &name, &familyname, &givenname, &suffix, &roles, &role_out, NULL );
 	return status;
 }
 
@@ -452,7 +509,7 @@
 
 	tag = ( school ) ? school_tag : address_tag;
 
-	fstatus = fields_add( info, tag, xml_data( node ), level );
+	fstatus = fields_add( info, tag, xml_value( node ), level );
 	if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 
 	return BIBL_OK;
@@ -462,26 +519,26 @@
 modsin_placeterm_code( xml *node, fields *info, int level )
 {
 	int fstatus, status = BIBL_OK;
-	newstr s, *auth;
+	str s, *auth;
 
-	newstr_init( &s );
+	str_init( &s );
 
 	auth = xml_getattrib( node, "authority" );
 	if ( auth && auth->len ) {
-		newstr_newstrcpy( &s, auth );
-		newstr_addchar( &s, '|' );
+		str_strcpy( &s, auth );
+		str_addchar( &s, '|' );
 	}
-	newstr_newstrcat( &s, node->value );
+	str_strcat( &s, node->value );
 
-	if ( newstr_memerr( &s ) ) {
+	if ( str_memerr( &s ) ) {
 		status = BIBL_ERR_MEMERR;
 		goto out;
 	}
 
-	fstatus = fields_add( info, "CODEDADDRESS", s.data, level );
+	fstatus = fields_add( info, "CODEDADDRESS", str_cstr( &s ), level );
 	if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
 out:
-	newstr_free( &s );
+	str_free( &s );
 	return status;
 }
 
@@ -489,13 +546,13 @@
 modsin_placeterm( xml *node, fields *info, int level, int school )
 {
 	int status = BIBL_OK;
-	newstr *type;
+	str *type;
 
 	type = xml_getattrib( node, "type" );
-	if ( type && type->len ) {
-		if ( !strcmp( type->data, "text" ) )
+	if ( str_has_value( type ) ) {
+		if ( !strcmp( str_cstr( type ), "text" ) )
 			status = modsin_placeterm_text( node, info, level, school );
-		else if ( !strcmp( type->data, "code" ) )
+		else if ( !strcmp( str_cstr( type ), "code" ) )
 			status = modsin_placeterm_code( node, info, level );
 	}
 
@@ -520,21 +577,21 @@
 }
 
 static int
-modsin_origininfor( xml *node, fields *info, int level, newstr *pub, newstr *add, newstr *addc, newstr *ed, newstr *iss )
+modsin_origininfor( xml *node, fields *info, int level, str *pub, str *add, str *addc, str *ed, str *iss )
 {
 	int status = BIBL_OK;
 	if ( xml_tagexact( node, "dateIssued" ) )
 		status = modsin_date( node, info, level, 0 );
-	else if ( xml_tagexact( node, "publisher" ) && xml_hasdata( node ) ) {
-		newstr_newstrcat( pub, node->value );
-		if ( newstr_memerr( pub ) ) return BIBL_ERR_MEMERR;
-	} else if ( xml_tagexact( node, "edition" ) && xml_hasdata( node ) ) {
-		newstr_newstrcat( ed, node->value );
-		if( newstr_memerr( ed ) ) return BIBL_ERR_MEMERR;
-	} else if ( xml_tagexact( node, "issuance" ) && xml_hasdata( node ) ) {
-		newstr_newstrcat( iss, node->value );
-		if ( newstr_memerr( iss ) ) return BIBL_ERR_MEMERR;
-	} else if ( xml_tagexact( node, "place" ) && xml_hasdata( node ) )
+	else if ( xml_tagexact( node, "publisher" ) && xml_hasvalue( node ) ) {
+		str_strcat( pub, node->value );
+		if ( str_memerr( pub ) ) return BIBL_ERR_MEMERR;
+	} else if ( xml_tagexact( node, "edition" ) && xml_hasvalue( node ) ) {
+		str_strcat( ed, node->value );
+		if( str_memerr( ed ) ) return BIBL_ERR_MEMERR;
+	} else if ( xml_tagexact( node, "issuance" ) && xml_hasvalue( node ) ) {
+		str_strcat( iss, node->value );
+		if ( str_memerr( iss ) ) return BIBL_ERR_MEMERR;
+	} else if ( xml_tagexact( node, "place" ) && xml_hasvalue( node ) )
 		status = modsin_placer( node, info, level, 0 );
 	if ( status!=BIBL_OK ) return status;
 	if ( node->down ) {
@@ -549,35 +606,35 @@
 static int
 modsin_origininfo( xml *node, fields *info, int level )
 {
-	newstr publisher, address, addcode, edition, issuance;
+	str publisher, address, addcode, edition, issuance;
 	int fstatus, status = BIBL_OK;
 	if ( node->down ) {
-		newstrs_init( &publisher, &address, &addcode, &edition, &issuance, NULL );
+		strs_init( &publisher, &address, &addcode, &edition, &issuance, NULL );
 		status = modsin_origininfor( node->down, info, level, &publisher, 
 				&address, &addcode, &edition, &issuance );
 		if ( status!=BIBL_OK ) goto out;
-		if ( publisher.len ) {
-			fstatus = fields_add( info, "PUBLISHER", publisher.data, level );
+		if ( str_has_value( &publisher ) ) {
+			fstatus = fields_add( info, "PUBLISHER", str_cstr( &publisher ), level );
 			if ( fstatus!=FIELDS_OK ) { status=BIBL_ERR_MEMERR; goto out; }
 		}
-		if ( address.len ) {
-			fstatus = fields_add( info, "ADDRESS", address.data, level );
+		if ( str_has_value( &address ) ) {
+			fstatus = fields_add( info, "ADDRESS", str_cstr( &address ), level );
 			if ( fstatus!=FIELDS_OK ) { status=BIBL_ERR_MEMERR; goto out; }
 		}
-		if ( addcode.len ) {
-			fstatus = fields_add( info, "CODEDADDRESS", addcode.data, level );
+		if ( str_has_value( &addcode ) ) {
+			fstatus = fields_add( info, "CODEDADDRESS", str_cstr( &addcode ), level );
 			if ( fstatus!=FIELDS_OK ) { status=BIBL_ERR_MEMERR; goto out; }
 		}
-		if ( edition.len ) {
-			fstatus = fields_add( info, "EDITION", edition.data, level );
+		if ( str_has_value( &edition ) ) {
+			fstatus = fields_add( info, "EDITION", str_cstr( &edition ), level );
 			if ( fstatus!=FIELDS_OK ) { status=BIBL_ERR_MEMERR; goto out; }
 		}
-		if ( issuance.len ) {
-			fstatus = fields_add( info, "ISSUANCE", issuance.data, level );
+		if ( str_has_value( &issuance ) ) {
+			fstatus = fields_add( info, "ISSUANCE", str_cstr( &issuance ), level );
 			if ( fstatus!=FIELDS_OK ) { status=BIBL_ERR_MEMERR; goto out; }
 		}
 out:
-		newstrs_free( &publisher, &address, &addcode, &edition, &issuance, NULL );
+		strs_free( &publisher, &address, &addcode, &edition, &issuance, NULL );
 	}
 	return status;
 }
@@ -586,7 +643,11 @@
 modsin_subjectr( xml *node, fields *info, int level )
 {
 	int fstatus, status = BIBL_OK;
-	if ( xml_tagexact( node, "topic" ) || xml_tagexact( node, "geographic" )) {
+	if ( xml_tag_attrib( node, "topic", "class", "primary" ) ) {
+		fstatus = fields_add( info, "EPRINTCLASS", node->value->data, level );
+		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+	}
+	else if ( xml_tagexact( node, "topic" ) || xml_tagexact( node, "geographic" )) {
 		fstatus = fields_add( info, "KEYWORD", node->value->data, level );
 		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
@@ -610,10 +671,10 @@
 modsin_id1( xml *node, fields *info, int level )
 {
 	int fstatus;
-	newstr *ns;
+	str *ns;
 	ns = xml_getattrib( node, "ID" );
-	if ( ns && ns->len ) {
-		fstatus = fields_add( info, "REFNUM", ns->data, level );
+	if ( str_has_value( ns ) ) {
+		fstatus = fields_add( info, "REFNUM", str_cstr( ns ), level );
 		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	return BIBL_OK;
@@ -633,8 +694,8 @@
 	int i, ismarc = 0, isadded = 0, fstatus;
 	char *d;
 
-	if ( !xml_hasdata( node ) ) return BIBL_OK;
-	d = xml_data( node );
+	if ( !xml_hasvalue( node ) ) return BIBL_OK;
+	d = xml_value( node );
 	if ( marc_findgenre( d )!=-1 ) ismarc = 1;
 	if ( !ismarc ) {
 		for ( i=0; i<nadded && ismarc==0 && isadded==0; ++i )
@@ -665,16 +726,16 @@
 	int fstatus, status = BIBL_OK;
 	char *d = NULL;
 	if ( xml_tagexact( node, "languageTerm" ) ) {
-		if ( xml_hasdata( node ) ) {
+		if ( xml_hasvalue( node ) ) {
 			if ( xml_hasattrib( node, "type", "code" ) ) {
 				if ( xml_hasattrib( node, "authority", "iso639-1" ) )
-					d = iso639_1_from_code( xml_data( node ) );
+					d = iso639_1_from_code( xml_value( node ) );
 				else if ( xml_hasattrib( node, "authority", "iso639-2b" ) )
-					d = iso639_2_from_code( xml_data( node ) );
+					d = iso639_2_from_code( xml_value( node ) );
 				else if ( xml_hasattrib( node, "authority", "iso639-3" ))
-					d = iso639_3_from_code( xml_data( node ) );
+					d = iso639_3_from_code( xml_value( node ) );
 			}
-			if ( !d ) d  = xml_data( node );
+			if ( !d ) d  = xml_value( node );
 			fstatus = fields_add( info, "LANGUAGE", d, level );
 			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 		}
@@ -688,8 +749,8 @@
 {
 	int fstatus, status = BIBL_OK;
 	/* Old versions of MODS had <language>English</language> */
-	if ( xml_hasdata( node ) ) {
-		fstatus = fields_add( info, "LANGUAGE", xml_data( node ), level );
+	if ( xml_hasvalue( node ) ) {
+		fstatus = fields_add( info, "LANGUAGE", xml_value( node ), level );
 		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 
@@ -702,8 +763,8 @@
 modsin_simple( xml *node, fields *info, char *tag, int level )
 {
 	int fstatus;
-	if ( xml_hasdata( node ) ) {
-		fstatus = fields_add( info, tag, xml_data( node ), level );
+	if ( xml_hasvalue( node ) ) {
+		fstatus = fields_add( info, tag, xml_value( node ), level );
 		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 	return BIBL_OK;
@@ -713,22 +774,29 @@
 modsin_locationr( xml *node, fields *info, int level )
 {
 	int fstatus, status = BIBL_OK;
+	char *url        = "URL";
+	char *fileattach = "FILEATTACH";
 	char *tag=NULL;
 
 	if ( xml_tagexact( node, "url" ) ) {
 		if ( xml_hasattrib( node, "access", "raw object" ) )
-			tag = "FILEATTACH";
+			tag = fileattach;
 		else
-			tag = "URL";
-	} else if ( xml_tagexact( node, "physicalLocation" ) ) {
+			tag = url;
+	}
+	else if ( xml_tagexact( node, "physicalLocation" ) ) {
 		if ( xml_hasattrib( node, "type", "school" ) )
 			tag = "SCHOOL";
 		else
 			tag = "LOCATION";
 	}
 
-	if ( tag ) {
-		fstatus = fields_add( info, tag, node->value->data, level );
+	if ( tag == url ) {
+		status = urls_split_and_add( xml_value( node ), info, level );
+		if ( status!=BIBL_OK ) return status;
+	}
+	else if ( tag ) {
+		fstatus = fields_add( info, tag, xml_value( node ), level );
 		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 
@@ -749,13 +817,13 @@
 }
 
 static int
-modsin_descriptionr( xml *node, newstr *s )
+modsin_descriptionr( xml *node, str *s )
 {
 	int status = BIBL_OK;
 	if ( xml_tagexact( node, "extent" ) ||
 	     xml_tagexact( node, "note" ) ) {
-		newstr_newstrcpy( s, node->value );
-		if ( newstr_memerr( s ) ) return BIBL_ERR_MEMERR;
+		str_strcpy( s, node->value );
+		if ( str_memerr( s ) ) return BIBL_ERR_MEMERR;
 	}
 	if ( node->down ) {
 		status = modsin_descriptionr( node->down, s );
@@ -769,29 +837,29 @@
 modsin_description( xml *node, fields *info, int level )
 {
 	int fstatus, status = BIBL_OK;
-	newstr s;
-	newstr_init( &s );
+	str s;
+	str_init( &s );
 	if ( node->down ) {
 		status = modsin_descriptionr( node->down, &s );
 		if ( status!=BIBL_OK ) goto out;
 	} else {
 		if ( node->value && node->value->len > 0 )
-			newstr_newstrcpy( &s, node->value );
-		if ( newstr_memerr( &s ) ) {
+			str_strcpy( &s, node->value );
+		if ( str_memerr( &s ) ) {
 			status = BIBL_ERR_MEMERR;
 			goto out;
 		}
 	}
-	if ( s.len ) {
-		fstatus = fields_add( info, "DESCRIPTION", s.data, level );
+	if ( str_has_value( &s ) ) {
+		fstatus = fields_add( info, "DESCRIPTION", str_cstr( &s ), level );
 		if ( fstatus!=FIELDS_OK ) {
 			status = BIBL_ERR_MEMERR;
 			goto out;
 		}
 	}
 out:
-	newstr_free( &s );
-	return BIBL_OK;
+	str_free( &s );
+	return status;
 }
 
 static int
@@ -824,8 +892,8 @@
 {
 	int fstatus, status = BIBL_OK;
 	char *tag, *d;
-	if ( xml_hasdata( node ) ) {
-		d = xml_data( node );
+	if ( xml_hasvalue( node ) ) {
+		d = xml_value( node );
 		if ( xml_tag_attrib( node, "classification", "authority", "lcc" ) )
 			tag = "LCC";
 		else
@@ -847,8 +915,8 @@
 	/* extract recordIdentifier */
 	curr = node;
 	while ( curr ) {
-		if ( xml_tagexact( curr, "recordIdentifier" ) && xml_hasdata( curr ) ) {
-			d = xml_data( curr );
+		if ( xml_tagexact( curr, "recordIdentifier" ) && xml_hasvalue( curr ) ) {
+			d = xml_value( curr );
 			fstatus = fields_add( info, "REFNUM", d, level );
 			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 		}
@@ -861,21 +929,24 @@
 modsin_identifier( xml *node, fields *info, int level )
 {
 	convert ids[] = {
-		{ "citekey",       "REFNUM"       },
-		{ "issn",          "ISSN"         },
-		{ "isbn",          "ISBN"         },
-		{ "doi",           "DOI"          },
-		{ "url",           "URL"          },
-		{ "uri",           "URL"          },
-		{ "pmid",          "PMID"         },
-		{ "pubmed",        "PMID"         },
-		{ "medline",       "MEDLINE"      },
-		{ "arXiv",         "ARXIV"        },
-		{ "pii",           "PII"          },
-		{ "isi",           "ISIREFNUM"    },
-		{ "serial number", "SERIALNUMBER" },
-		{ "accessnum",     "ACCESSNUM"    },
-		{ "jstor",         "JSTOR"        },
+		{ "citekey",       "REFNUM",      0, 0 },
+		{ "issn",          "ISSN",        0, 0 },
+		{ "coden",         "CODEN",       0, 0 },
+		{ "isbn",          "ISBN",        0, 0 },
+		{ "doi",           "DOI",         0, 0 },
+		{ "url",           "URL",         0, 0 },
+		{ "uri",           "URL",         0, 0 },
+		{ "pmid",          "PMID",        0, 0 },
+		{ "pubmed",        "PMID",        0, 0 },
+		{ "medline",       "MEDLINE",     0, 0 },
+		{ "pmc",           "PMC",         0, 0 },
+		{ "arXiv",         "ARXIV",       0, 0 },
+		{ "MRnumber",      "MRNUMBER",    0, 0 },
+		{ "pii",           "PII",         0, 0 },
+		{ "isi",           "ISIREFNUM",   0, 0 },
+		{ "serial number", "SERIALNUMBER",0, 0 },
+		{ "accessnum",     "ACCESSNUM",   0, 0 },
+		{ "jstor",         "JSTOR",       0, 0 },
 	};
 	int i, fstatus, n = sizeof( ids ) / sizeof( ids[0] );
 	if ( !node->value || node->value->len==0 ) return BIBL_OK;
@@ -892,11 +963,11 @@
 modsin_mods( xml *node, fields *info, int level )
 {
 	convert simple[] = {
-		{ "note",            "NOTES" },
-		{ "abstract",        "ABSTRACT" },
-		{ "bibtex-annote",   "ANNOTE" },
-		{ "typeOfResource",  "RESOURCE" },
-		{ "tableOfContents", "CONTENTS" },
+		{ "note",            "NOTES",    0, 0 },
+		{ "abstract",        "ABSTRACT", 0, 0 },
+		{ "bibtex-annote",   "ANNOTE",   0, 0 },
+		{ "typeOfResource",  "RESOURCE", 0, 0 },
+		{ "tableOfContents", "CONTENTS", 0, 0 },
 	};
 	int nsimple = sizeof( simple ) / sizeof( simple[0] );
 	int i, found = 0, status = BIBL_OK;
@@ -944,6 +1015,10 @@
 			  xml_tag_attrib( node, "relatedItem", "type", "series" ) ) {
 			if ( node->down ) status = modsin_mods( node->down, info, level+1 );
 		}
+		else if ( xml_tag_attrib( node, "relatedItem", "type", "original" ) ) {
+			if ( node->down ) status = modsin_mods( node->down, info, LEVEL_ORIG );
+		}
+
 		if ( status!=BIBL_OK ) return status;
 	}
 
@@ -971,8 +1046,8 @@
 	return status;
 }
 
-int
-modsin_processf( fields *modsin, char *data, char *filename, long nref )
+static int
+modsin_processf( fields *modsin, char *data, char *filename, long nref, param *p )
 {
 	int status;
 	xml top;
@@ -1011,32 +1086,31 @@
 	return xml_findend( p, "mods" );
 }
 
-int
-modsin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line,
-		newstr *reference, int *fcharset )
+static int
+modsin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
-	newstr tmp;
+	str tmp;
 	int m, file_charset = CHARSET_UNKNOWN;
 	char *startptr = NULL, *endptr = NULL;
 
-	newstr_init( &tmp );
+	str_init( &tmp );
 
 	do {
-		if ( line->data ) newstr_newstrcat( &tmp, line );
-		if ( tmp.data ) {
+		if ( line->data ) str_strcat( &tmp, line );
+		if ( str_has_value( &tmp ) ) {
 			m = xml_getencoding( &tmp );
 			if ( m!=CHARSET_UNKNOWN ) file_charset = m;
 			startptr = modsin_startptr( tmp.data );
 			endptr = modsin_endptr( tmp.data );
 		} else startptr = endptr = NULL;
-		newstr_empty( line );
+		str_empty( line );
 		if ( startptr && endptr ) {
-			newstr_segcpy( reference, startptr, endptr );
-			newstr_strcpy( line, endptr );
+			str_segcpy( reference, startptr, endptr );
+			str_strcpyc( line, endptr );
 		}
-	} while ( !endptr && newstr_fget( fp, buf, bufsize, bufpos, line ) );
+	} while ( !endptr && str_fget( fp, buf, bufsize, bufpos, line ) );
 
-	newstr_free( &tmp );
+	str_free( &tmp );
 	*fcharset = file_charset;
 	return ( reference->len > 0 );
 }
diff --git a/bibutils/modsin.h b/bibutils/modsin.h
deleted file mode 100644
--- a/bibutils/modsin.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * modsin.h
- *
- * Copyright (c) Chris Putnam 2004-2014
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef MODSIN_H
-#define MODSIN_H
-
-#include "newstr.h"
-#include "fields.h"
-#include "reftypes.h"
-
-extern int modsin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset );
-extern int modsin_processf( fields *medin, char *data, char *filename, long nref );
-extern void modsin_initparams( param *p, const char *progname );
-
-#endif
diff --git a/bibutils/modsout.c b/bibutils/modsout.c
--- a/bibutils/modsout.c
+++ b/bibutils/modsout.c
@@ -1,25 +1,30 @@
 /*
  * modsout.c
  *
- * Copyright (c) Chris Putnam 2003-2014
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Source code released under the GPL version 2
  *
  */
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <string.h>
 #include "is_ws.h"
-#include "newstr.h"
+#include "str.h"
 #include "charsets.h"
-#include "newstr_conv.h"
+#include "str_conv.h"
 #include "fields.h"
 #include "iso639_2.h"
 #include "utf8.h"
-#include "modsout.h"
 #include "modstypes.h"
 #include "marc.h"
+#include "bibformats.h"
 
+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 )
 {
@@ -41,96 +46,134 @@
 	p->writef  = modsout_write;
 }
 
-static int
-increment_level( int level, int amt )
-{
-	if ( level > -1 ) return level+amt;
-	else return level-amt;
-}
+/* output_tag()
+ *
+ * mode = TAG_OPEN,         "<tag>"
+ * mode = TAG_CLOSE,        "</tag>"
+ * mode = TAG_OPENCLOSE,    "<tag>data</tag>"
+ * mode = TAG_SELFCLOSE,    "<tag/>"
+ *
+ * newline = TAG_NONEWLINE, "<tag>"
+ * newline = TAG_NEWLINE,   "<tag>\n"
+ *
+ */
+#define TAG_OPEN      (0)
+#define TAG_CLOSE     (1)
+#define TAG_OPENCLOSE (2)
+#define TAG_SELFCLOSE (3)
 
+#define TAG_NONEWLINE (0)
+#define TAG_NEWLINE   (1)
+
 static void
-output_tab0( FILE *outptr, int level )
+output_tag_core( FILE *outptr, int nindents, char *tag, char *data, unsigned char mode, unsigned char newline, va_list *attrs )
 {
+	char *attr, *val;
 	int i;
-	level = abs( level );
-	for ( i=0; i<=level; ++i ) fprintf( outptr, "    " );
-}
 
-static void
-output_tab1( FILE *outptr, int level, char *tag )
-{
-	output_tab0( outptr, level );
+	for ( i=0; i<nindents; ++i ) fprintf( outptr, "    " );
+
+	if ( mode!=TAG_CLOSE )
+		fprintf( outptr, "<" );
+	else
+		fprintf( outptr, "</" );
+
 	fprintf( outptr, "%s", tag );
-}
 
-static void
-output_tab2_attrib( FILE *outptr, int level, char *tag, char *data, 
-	char *attrib, char *type, int cr )
-{
-	output_tab0( outptr, level );
-	fprintf( outptr, "<%s", tag );
-	if ( attrib && type ) fprintf( outptr, " %s=\"%s\"", attrib, type );
-	fprintf( outptr, ">%s</%s>", data, tag );
-	if ( cr ) fprintf( outptr, "\n" );
-}
+	do {
+		attr = va_arg( *attrs, char * );
+		if ( attr ) val  = va_arg( *attrs, char * );
+		if ( attr && val )
+			fprintf( outptr, " %s=\"%s\"", attr, val );
+	} while ( attr && val );
 
-static void
-output_tab4( FILE *outptr, int level, char *tag, char *aname, char *avalue,
-		char *data, int cr )
-{
-	output_tab0( outptr, level );
-	fprintf( outptr, "<%s %s=\"%s\">%s</%s>", tag,aname,avalue,data,tag);
-	if ( cr ) fprintf( outptr, "\n" );
+	if ( mode!=TAG_SELFCLOSE )
+		fprintf( outptr, ">" );
+	else
+		fprintf( outptr, "/>" );
+
+	if ( mode==TAG_OPENCLOSE ) {
+		fprintf( outptr, "%s</%s>", data, tag );
+	}
+
+	if ( newline==TAG_NEWLINE )
+		fprintf( outptr, "\n" );
 }
 
+/* output_tag()
+ *
+ *     output XML tag
+ *
+ * mode     = [ TAG_OPEN | TAG_CLOSE | TAG_OPENCLOSE | TAG_SELFCLOSE ]
+ * newline  = [ TAG_NEWLINE | TAG_NONEWLINE ]
+ *
+ * for mode TAG_OPENCLOSE, ensure that value is non-NULL, as string pointed to by value
+ * will be output in the tag
+ */
 static void
-output_tab6( FILE *outptr, int level, char *tag, char *aname, char *avalue,
-		char *bname, char *bvalue, char *data, int cr )
+output_tag( FILE *outptr, int nindents, char *tag, char *value, unsigned char mode, unsigned char newline, ... )
 {
-	output_tab0( outptr, level );
-	fprintf( outptr, "<%s %s=\"%s\" %s=\"%s\">%s</%s>", tag,aname,avalue,bname,bvalue,data,tag);
-	if ( cr ) fprintf( outptr, "\n" );
+	va_list attrs;
+
+	va_start( attrs, newline );
+	output_tag_core( outptr, nindents, tag, value, mode, newline, &attrs );
+	va_end( attrs );
 }
 
+/* output_fil()
+ *
+ *     output XML tag, but lookup data in fields struct
+ *
+ * mode     = [ TAG_OPEN | TAG_CLOSE | TAG_OPENCLOSE | TAG_SELFCLOSE ]
+ * newline  = [ TAG_NEWLINE | TAG_NONEWLINE ]
+ *
+ * value looked up in fields will only be used in mode TAG_OPENCLOSE
+ */
 static void
-output_fill2( FILE *outptr, int level, char *tag, fields *f, int n, int cr )
+output_fil( FILE *outptr, int nindents, char *tag, fields *f, int n, unsigned char mode, unsigned char newline, ... )
 {
+	va_list attrs;
 	char *value;
+
 	if ( n!=-1 ) {
-		value = fields_value( f, n, FIELDS_CHRP );
-		output_tab2_attrib( outptr, level, tag, value, 
-			NULL, NULL, cr );
-		fields_setused( f, n );
+		value = (char *) fields_value( f, n, FIELDS_CHRP );
+		va_start( attrs, newline );
+		output_tag_core( outptr, nindents, tag, value, mode, newline, &attrs );
+		va_end( attrs );
 	}
 }
 
-static void
-output_fill4( FILE *outptr, int level, char *tag, char *aname, char *avalue,
-		fields *f, int n, int cr )
+static inline int
+lvl2indent( int level )
 {
-	char *value;
-	if ( n!=-1 ) {
-		value = fields_value( f, n, FIELDS_CHRP );
-		output_tab4( outptr, level, tag, aname, avalue,
-				value, cr );
-		fields_setused( f, n );
-	}
+	if ( level < -1 ) return -level + 1;
+	else return level + 1;
 }
 
-/*
- * Find the positions of all convert.internal tags and store the
- * locations in convert.code.
+static inline int
+incr_level( int level, int amt )
+{
+	if ( level > -1 ) return level+amt;
+	else return level-amt;
+}
+
+/* convert_findallfields()
  *
- * Return number of the tags found
+ *       Find the positions of all convert.internal tags in the fields
+ *       structure and store the locations in convert.pos element.
+ *
+ *       Return number of the tags found.
  */
 static int
-find_alltags( fields *f, convert *parts, int nparts, int level )
+convert_findallfields( fields *f, convert *parts, int nparts, int level )
 {
-	int i, n=0;
+	int i, n = 0;
+
 	for ( i=0; i<nparts; ++i ) {
-		parts[i].code = fields_find( f, parts[i].internal, level );
-		n += ( parts[i].code!=-1 );
+		parts[i].pos = fields_find( f, parts[i].internal, level );
+		n += ( parts[i].pos!=-1 );
 	}
+
 	return n;
 }
 
@@ -141,83 +184,76 @@
 	int subttl = fields_find( f, "SUBTITLE", level );
 	int shrttl = fields_find( f, "SHORTTITLE", level );
 	int parttl = fields_find( f, "PARTTITLE", level );
+	char *val;
 
-	output_tab1( outptr, level, "<titleInfo>\n" );
-	output_fill2( outptr, increment_level(level,1), "title", f, ttl, 1 );
-	output_fill2( outptr, increment_level(level,1), "subTitle", f, subttl, 1 );
-	output_fill2( outptr, increment_level(level,1), "partName", f, parttl, 1 );
+	output_tag( outptr, lvl2indent(level),               "titleInfo", NULL,      TAG_OPEN,      TAG_NEWLINE, NULL );
+	output_fil( outptr, lvl2indent(incr_level(level,1)), "title",     f, ttl,    TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+	output_fil( outptr, lvl2indent(incr_level(level,1)), "subTitle",  f, subttl, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+	output_fil( outptr, lvl2indent(incr_level(level,1)), "partName",  f, parttl, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+	/* MODS output doesn't verify if we don't at least have a <title/> element */
 	if ( ttl==-1 && subttl==-1 )
-		output_tab1( outptr, increment_level(level,1), "<title/>\n" );
-	output_tab1( outptr, level, "</titleInfo>\n" );
+		output_tag( outptr, lvl2indent(incr_level(level,1)), "title", NULL,  TAG_SELFCLOSE, TAG_NEWLINE, NULL );
+	output_tag( outptr, lvl2indent(level),               "titleInfo", NULL,      TAG_CLOSE,     TAG_NEWLINE, NULL );
 
 	/* output shorttitle if it's different from normal title */
 	if ( shrttl!=-1 ) {
-		if ( ttl==-1 || subttl!=-1 ||
-			strcmp(f->data[ttl].data,f->data[shrttl].data) ) {
-			output_tab1( outptr, level, 
-					"<titleInfo type=\"abbreviated\">\n" );
-			output_fill2( outptr, level+1, "title", f, shrttl,1);
-			output_tab1( outptr, level, "</titleInfo>\n" );
+		val = (char *) fields_value( f, shrttl, FIELDS_CHRP );
+		if ( ttl==-1 || subttl!=-1 || strcmp(f->data[ttl].data,val) ) {
+			output_tag( outptr, lvl2indent(level),               "titleInfo", NULL, TAG_OPEN,      TAG_NEWLINE, "type", "abbreviated", NULL );
+			output_tag( outptr, lvl2indent(incr_level(level,1)), "title",     val,  TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+			output_tag( outptr, lvl2indent(level),               "titleInfo", NULL, TAG_CLOSE,     TAG_NEWLINE, NULL );
 		}
-		fields_setused( f, shrttl );
 	}
 }
 
 static void
-output_personalstart( FILE *outptr, int level )
-{
-	int j;
-	for ( j=0; j<=level; ++j ) fprintf( outptr, "    " );
-		fprintf( outptr, "<name type=\"personal\">\n" );
-}
-
-static void
 output_name( FILE *outptr, char *p, int level )
 {
-	newstr family, part, suffix;
+	str family, part, suffix;
 	int n=0;
 
-	newstrs_init( &family, &part, &suffix, NULL );
+	strs_init( &family, &part, &suffix, NULL );
 
-	while ( *p && *p!='|' ) newstr_addchar( &family, *p++ );
+	while ( *p && *p!='|' ) str_addchar( &family, *p++ );
 	if ( *p=='|' ) p++;
 
 	while ( *p ) {
-		while ( *p && *p!='|' ) newstr_addchar( &part, *p++ );
+		while ( *p && *p!='|' ) str_addchar( &part, *p++ );
 		/* truncate periods from "A. B. Jones" names */
 		if ( part.len ) {
 			if ( part.len==2 && part.data[1]=='.' ) {
 				part.len=1;
 				part.data[1]='\0';
 			}
-			if ( n==0 ) output_personalstart( outptr, level );
-			output_tab4( outptr, increment_level(level,1), "namePart", "type", 
-					"given", part.data, 1 );
+			if ( n==0 )
+				output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "personal", NULL );
+			output_tag( outptr, lvl2indent(incr_level(level,1)), "namePart", part.data, TAG_OPENCLOSE, TAG_NEWLINE, "type", "given", NULL );
 			n++;
 		}
 		if ( *p=='|' ) {
 			p++;
 			if ( *p=='|' ) {
 				p++;
-				while ( *p && *p!='|' ) newstr_addchar( &suffix, *p++ );
+				while ( *p && *p!='|' ) str_addchar( &suffix, *p++ );
 			}
-			newstr_empty( &part );
+			str_empty( &part );
 		}
 	}
 
 	if ( family.len ) {
-		if ( n==0 ) output_personalstart( outptr, level );
-		output_tab4( outptr, increment_level(level,1), "namePart", "type", "family",
-				family.data, 1 );
+		if ( n==0 )
+			output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "personal", NULL );
+		output_tag( outptr, lvl2indent(incr_level(level,1)), "namePart", family.data, TAG_OPENCLOSE, TAG_NEWLINE, "type", "family", NULL );
+		n++;
 	}
 
 	if ( suffix.len ) {
-		if ( n==0 ) output_personalstart( outptr, level );
-		output_tab4( outptr, increment_level(level,1), "namePart", "type", "suffix",
-				suffix.data, 1 );
+		if ( n==0 )
+			output_tag( outptr, lvl2indent(level), "name", NULL, TAG_OPEN, TAG_NEWLINE, "type", "personal", NULL );
+		output_tag( outptr, lvl2indent(incr_level(level,1)), "namePart", suffix.data, TAG_OPENCLOSE, TAG_NEWLINE, "type", "suffix", NULL );
 	}
 
-	newstrs_free( &part, &family, &suffix, NULL );
+	strs_free( &part, &family, &suffix, NULL );
 }
 
 
@@ -241,140 +277,166 @@
 output_names( fields *f, FILE *outptr, int level )
 {
 	convert   names[] = {
-	  { "author",                              "AUTHOR",          MARC_AUTHORITY },
-	  { "editor",                              "EDITOR",          MARC_AUTHORITY },
-	  { "annotator",                           "ANNOTATOR",       MARC_AUTHORITY },
-	  { "artist",                              "ARTIST",          MARC_AUTHORITY },
-	  { "author",                              "2ND_AUTHOR",      MARC_AUTHORITY },
-	  { "author",                              "3RD_AUTHOR",      MARC_AUTHORITY },
-	  { "author",                              "SUB_AUTHOR",      MARC_AUTHORITY },
-	  { "author",                              "COMMITTEE",       MARC_AUTHORITY },
-	  { "author",                              "COURT",           MARC_AUTHORITY },
-	  { "author",                              "LEGISLATIVEBODY", MARC_AUTHORITY },
-	  { "author of afterword, colophon, etc.", "AFTERAUTHOR",     MARC_AUTHORITY },
-	  { "author of introduction, etc.",        "INTROAUTHOR",     MARC_AUTHORITY },
-	  { "cartographer",                        "CARTOGRAPHER",    MARC_AUTHORITY },
-	  { "collaborator",                        "COLLABORATOR",    MARC_AUTHORITY },
-	  { "commentator",                         "COMMENTATOR",     MARC_AUTHORITY },
-	  { "compiler",                            "COMPILER",        MARC_AUTHORITY },
-	  { "degree grantor",                      "DEGREEGRANTOR",   MARC_AUTHORITY },
-	  { "director",                            "DIRECTOR",        MARC_AUTHORITY },
-	  { "event",                               "EVENT",           NO_AUTHORITY   },
-	  { "inventor",                            "INVENTOR",        MARC_AUTHORITY },
-	  { "organizer of meeting",                "ORGANIZER",       MARC_AUTHORITY },
-	  { "patent holder",                       "ASSIGNEE",        MARC_AUTHORITY },
-	  { "performer",                           "PERFORMER",       MARC_AUTHORITY },
-	  { "producer",                            "PRODUCER",        MARC_AUTHORITY },
-	  { "recipient",                           "RECIPIENT",       MARC_AUTHORITY },
-	  { "redactor",                            "REDACTOR",        MARC_AUTHORITY },
-	  { "reporter",                            "REPORTER",        MARC_AUTHORITY },
-	  { "sponsor",                             "SPONSOR",         MARC_AUTHORITY },
-	  { "translator",                          "TRANSLATOR",      MARC_AUTHORITY },
-	  { "writer",                              "WRITER",          MARC_AUTHORITY },
+	  { "author",                              "AUTHOR",          0, MARC_AUTHORITY },
+	  { "editor",                              "EDITOR",          0, MARC_AUTHORITY },
+	  { "annotator",                           "ANNOTATOR",       0, MARC_AUTHORITY },
+	  { "artist",                              "ARTIST",          0, MARC_AUTHORITY },
+	  { "author",                              "2ND_AUTHOR",      0, MARC_AUTHORITY },
+	  { "author",                              "3RD_AUTHOR",      0, MARC_AUTHORITY },
+	  { "author",                              "SUB_AUTHOR",      0, MARC_AUTHORITY },
+	  { "author",                              "COMMITTEE",       0, MARC_AUTHORITY },
+	  { "author",                              "COURT",           0, MARC_AUTHORITY },
+	  { "author",                              "LEGISLATIVEBODY", 0, MARC_AUTHORITY },
+	  { "author of afterword, colophon, etc.", "AFTERAUTHOR",     0, MARC_AUTHORITY },
+	  { "author of introduction, etc.",        "INTROAUTHOR",     0, MARC_AUTHORITY },
+	  { "cartographer",                        "CARTOGRAPHER",    0, MARC_AUTHORITY },
+	  { "collaborator",                        "COLLABORATOR",    0, MARC_AUTHORITY },
+	  { "commentator",                         "COMMENTATOR",     0, MARC_AUTHORITY },
+	  { "compiler",                            "COMPILER",        0, MARC_AUTHORITY },
+	  { "degree grantor",                      "DEGREEGRANTOR",   0, MARC_AUTHORITY },
+	  { "director",                            "DIRECTOR",        0, MARC_AUTHORITY },
+	  { "event",                               "EVENT",           0, NO_AUTHORITY   },
+	  { "inventor",                            "INVENTOR",        0, MARC_AUTHORITY },
+	  { "organizer of meeting",                "ORGANIZER",       0, MARC_AUTHORITY },
+	  { "patent holder",                       "ASSIGNEE",        0, MARC_AUTHORITY },
+	  { "performer",                           "PERFORMER",       0, MARC_AUTHORITY },
+	  { "producer",                            "PRODUCER",        0, MARC_AUTHORITY },
+	  { "recipient",                           "RECIPIENT",       0, MARC_AUTHORITY },
+	  { "redactor",                            "REDACTOR",        0, MARC_AUTHORITY },
+	  { "reporter",                            "REPORTER",        0, MARC_AUTHORITY },
+	  { "sponsor",                             "SPONSOR",         0, MARC_AUTHORITY },
+	  { "translator",                          "TRANSLATOR",      0, MARC_AUTHORITY },
+	  { "writer",                              "WRITER",          0, MARC_AUTHORITY },
 	};
 	int i, n, nfields, ntypes = sizeof( names ) / sizeof( convert );
 	int f_asis, f_corp, f_conf;
-	newstr role;
+	str role;
 
-	newstr_init( &role );
+	str_init( &role );
 	nfields = fields_num( f );
 	for ( n=0; n<ntypes; ++n ) {
 		for ( i=0; i<nfields; ++i ) {
 			if ( fields_level( f, i )!=level ) continue;
 			if ( f->data[i].len==0 ) continue;
 			f_asis = f_corp = f_conf = 0;
-			newstr_strcpy( &role, f->tag[i].data );
-			if ( newstr_findreplace( &role, ":ASIS", "" )) f_asis=1;
-			if ( newstr_findreplace( &role, ":CORP", "" )) f_corp=1;
-			if ( newstr_findreplace( &role, ":CONF", "" )) f_conf=1;
+			str_strcpyc( &role, f->tag[i].data );
+			if ( str_findreplace( &role, ":ASIS", "" )) f_asis=1;
+			if ( str_findreplace( &role, ":CORP", "" )) f_corp=1;
+			if ( str_findreplace( &role, ":CONF", "" )) f_conf=1;
 			if ( strcasecmp( role.data, names[n].internal ) )
 				continue;
 			if ( f_asis ) {
-				output_tab0( outptr, level );
-				fprintf( outptr, "<name>\n" );
-				output_fill2( outptr, increment_level(level,1), "namePart", f, i, 1 );
+				output_tag( outptr, lvl2indent(level),               "name",     NULL, TAG_OPEN,      TAG_NEWLINE, NULL );
+				output_fil( outptr, lvl2indent(incr_level(level,1)), "namePart", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 			} else if ( f_corp ) {
-				output_tab0( outptr, level );
-				fprintf( outptr, "<name type=\"corporate\">\n" );
-				output_fill2( outptr, increment_level(level,1), "namePart", f, i, 1 );
+				output_tag( outptr, lvl2indent(level),               "name",     NULL, TAG_OPEN,      TAG_NEWLINE, "type", "corporate", NULL );
+				output_fil( outptr, lvl2indent(incr_level(level,1)), "namePart", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 			} else if ( f_conf ) {
-				output_tab0( outptr, level );
-				fprintf( outptr, "<name type=\"conference\">\n" );
-				output_fill2( outptr, increment_level(level,1), "namePart", f, i, 1 );
+				output_tag( outptr, lvl2indent(level),               "name",     NULL, TAG_OPEN,      TAG_NEWLINE, "type", "conference", NULL );
+				output_fil( outptr, lvl2indent(incr_level(level,1)), "namePart", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 			} else {
 				output_name(outptr, f->data[i].data, level);
 			}
-			output_tab1( outptr, increment_level(level,1), "<role>\n" );
-			output_tab1( outptr, increment_level(level,2), "<roleTerm" );
+			output_tag( outptr, lvl2indent(incr_level(level,1)), "role", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
 			if ( names[n].code & MARC_AUTHORITY )
-				fprintf( outptr, " authority=\"marcrelator\"");
-			fprintf( outptr, " type=\"text\">");
-			fprintf( outptr, "%s", names[n].mods );
-			fprintf( outptr, "</roleTerm>\n");
-			output_tab1( outptr, increment_level(level,1), "</role>\n" );
-			output_tab1( outptr, level, "</name>\n" );
+				output_tag( outptr, lvl2indent(incr_level(level,2)), "roleTerm", names[n].mods, TAG_OPENCLOSE, TAG_NEWLINE, "authority", "marcrelator", "type", "text", NULL );
+			else
+				output_tag( outptr, lvl2indent(incr_level(level,2)), "roleTerm", names[n].mods, TAG_OPENCLOSE, TAG_NEWLINE, "type", "text", NULL );
+			output_tag( outptr, lvl2indent(incr_level(level,1)), "role", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
+			output_tag( outptr, lvl2indent(level),               "name", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
 			fields_setused( f, i );
 		}
 	}
-	newstr_free( &role );
+	str_free( &role );
 }
 
+/* datepos[ NUM_DATE_TYPES ]
+ *     use define to ensure that the array and loops don't get out of sync
+ *     datepos[0] -> DATE:YEAR/PARTDATE:YEAR
+ *     datepos[1] -> DATE:MONTH/PARTDATE:MONTH
+ *     datepos[2] -> DATE:DAY/PARTDATE:DAY
+ *     datepos[3] -> DATE/PARTDATE
+ */
+#define DATE_YEAR      (0)
+#define DATE_MONTH     (1)
+#define DATE_DAY       (2)
+#define DATE_ALL       (3)
+#define NUM_DATE_TYPES (4)
+
 static int
-output_finddateissued( fields *f, int level, int pos[] )
+find_datepos( fields *f, int level, unsigned char use_altnames, int datepos[NUM_DATE_TYPES] )
 {
-	char      *src_names[] = { "YEAR", "MONTH", "DAY", "DATE" };
-	char      *alt_names[] = { "PARTYEAR", "PARTMONTH", "PARTDAY", "PARTDATE" };
-	int       i, found = -1, ntypes = 4;
+	char      *src_names[] = { "DATE:YEAR", "DATE:MONTH", "DATE:DAY", "DATE" };
+	char      *alt_names[] = { "PARTDATE:YEAR", "PARTDATE:MONTH", "PARTDATE:DAY", "PARTDATE" };
+	int       found = 0;
+	int       i;
 
-	for ( i=0; i<ntypes; ++i ) {
-		pos[i] = fields_find( f, src_names[i], level );
-		if ( pos[i]!=-1 ) found = pos[i];
-	}
-	/* for LEVEL_MAIN, do what it takes to find a date */
-	if ( found==-1 && level==0 ) {
-		for ( i=0; i<ntypes; ++i ) {
-			pos[i] = fields_find( f, src_names[i], -1 );
-			if ( pos[i]!=-1 ) found = pos[i];
-		}
-	}
-	if ( found==-1 && level==0 ) {
-		for ( i=0; i<ntypes; ++i ) {
-			pos[i] = fields_find( f, alt_names[i], -1 );
-			if ( pos[i]!=-1 ) found = pos[i];
-		}
+	for ( i=0; i<NUM_DATE_TYPES; ++i ) {
+		if ( !use_altnames )
+			datepos[i] = fields_find( f, src_names[i], level );
+		else
+			datepos[i] = fields_find( f, alt_names[i], level );
+		if ( datepos[i]!=-1 ) found = 1;
 	}
+
 	return found;
 }
 
-static void
-output_datepieces( fields *f, FILE *outptr, int pos[4] )
+/* find_dateinfo()
+ *
+ *      fill datepos[] array with position indexes to date information in fields *f
+ *
+ *      when generating dates for LEVEL_MAIN, first look at level=LEVEL_MAIN, but if that
+ *      fails, use LEVEL_ANY (-1)
+ *
+ *      returns 1 if date information found, 0 otherwise
+ */
+static int
+find_dateinfo( fields *f, int level, int datepos[ NUM_DATE_TYPES ] )
 {
-	int nprinted = 0, i;
-	for ( i=0; i<3 && pos[i]!=-1; ++i ) {
-		if ( nprinted>0 ) fprintf( outptr, "-" );
-		if ( i>0 && f->data[pos[i]].len==1 )
-			fprintf( outptr, "0" ); /*zero pad Jan,Feb,etc*/
-		fprintf( outptr,"%s",f->data[pos[i]].data );
-		nprinted++;
-		fields_setused( f, pos[i] );
+	int found;
+
+	/* default to finding date information for the current level */
+	found = find_datepos( f, level, 0, datepos );
+
+	/* for LEVEL_MAIN, do whatever it takes to find a date */
+	if ( !found && level == LEVEL_MAIN ) {
+		found = find_datepos( f, -1, 0, datepos );
 	}
+	if ( !found && level == LEVEL_MAIN ) {
+		found = find_datepos( f, -1, 1, datepos );
+	}
+
+	return found;
 }
 
 static void
-output_dateall( fields *f, FILE *outptr, int pos )
+output_datepieces( fields *f, FILE *outptr, int pos[ NUM_DATE_TYPES ] )
 {
-	fprintf( outptr, "%s", f->data[pos].data );
-	fields_setused( f, pos );
+	str *s;
+	int i;
+
+	for ( i=0; i<3 && pos[i]!=-1; ++i ) {
+		if ( i>0 ) fprintf( outptr, "-" );
+		/* zero pad month or days written as "1", "2", "3" ... */
+		if ( i==DATE_MONTH || i==DATE_DAY ) {
+			s = fields_value( f, pos[i], FIELDS_STRP_NOUSE );
+			if ( s->len==1 ) {
+				fprintf( outptr, "0" );
+			}
+		}
+		fprintf( outptr, "%s", (char *) fields_value( f, pos[i], FIELDS_CHRP ) );
+	}
 }
 
 static void
-output_dateissued( fields *f, FILE *outptr, int level, int pos[4] )
+output_dateissued( fields *f, FILE *outptr, int level, int pos[ NUM_DATE_TYPES ] )
 {
-	output_tab1( outptr, increment_level(level,1), "<dateIssued>" );
-	if ( pos[0]!=-1 || pos[1]!=-1 || pos[2]!=-1 ) {
+	output_tag( outptr, lvl2indent(incr_level(level,1)), "dateIssued", NULL, TAG_OPEN, TAG_NONEWLINE, NULL );
+	if ( pos[ DATE_YEAR ]!=-1 || pos[ DATE_MONTH ]!=-1 || pos[ DATE_DAY ]!=-1 ) {
 		output_datepieces( f, outptr, pos );
 	} else {
-		output_dateall( f, outptr, pos[3] );
+		fprintf( outptr, "%s", (char *) fields_value( f, pos[ DATE_ALL ], FIELDS_CHRP ) );
 	}
 	fprintf( outptr, "</dateIssued>\n" );
 }
@@ -382,73 +444,83 @@
 static void
 output_origin( fields *f, FILE *outptr, int level )
 {
-	convert origin[] = {
-		{ "issuance",	  "ISSUANCE",	0 },
-		{ "publisher",	  "PUBLISHER",	0 },
-		{ "place",	  "ADDRESS",	1 },
-		{ "place",	  "AUTHORADDRESS",	0 },
-		{ "edition",	  "EDITION",	0 },
-		{ "dateCaptured", "URLDATE",    0 }
+	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 }
 	};
-	int n, ntypes = sizeof( origin ) / sizeof ( convert );
-	int found, datefound, pos[5], date[4];
+	int nparts = sizeof( parts ) / sizeof( parts[0] );
+	int i, found, datefound, datepos[ NUM_DATE_TYPES ];
 
-	/* find all information to be outputted */
-	found = -1;
-	for ( n=0; n<ntypes; ++n ) {
-		pos[n] = fields_find( f, origin[n].internal, level );
-		if ( pos[n]!=-1 ) found = pos[n];
-	}
-	datefound = output_finddateissued( f, level, date );
-	if ( found==-1 && datefound==-1 ) return;
+	found     = convert_findallfields( f, parts, nparts, level );
+	datefound = find_dateinfo( f, level, datepos );
+	if ( !found && !datefound ) return;
 
-	output_tab1( outptr, level, "<originInfo>\n" );
-	output_fill2( outptr, increment_level(level,1), "issuance", f, pos[0], 1 );
-	if ( datefound!=-1 ) output_dateissued( f, outptr, level, date );
 
-	for ( n=1; n<ntypes; n++ ) {
-		if ( pos[n]==-1 ) continue;
-		output_tab0( outptr, increment_level(level,1) );
-		fprintf( outptr, "<%s", origin[n].mods );
-		fprintf( outptr, ">" );
-		if ( origin[n].code ) {
-			fprintf( outptr, "\n" );
-			output_fill4( outptr, increment_level(level,2), 
-				"placeTerm", "type", "text", f, pos[n], 1 );
-			output_tab0( outptr, increment_level(level,1) );
-		} else {
-			fprintf( outptr, "%s", f->data[pos[n]].data );
-			fields_setused( f, pos[n] );
+	output_tag( outptr, lvl2indent(level), "originInfo", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
+
+	/* issuance must precede date */
+	if ( parts[0].pos!=-1 )
+		output_fil( outptr, lvl2indent(incr_level(level,1)), "issuance", f, parts[0].pos, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+
+	/* date */
+	if ( datefound )
+		output_dateissued( f, outptr, level, datepos );
+
+	/* rest of the originInfo elements */
+	for ( i=1; i<nparts; i++ ) {
+
+		/* skip missing originInfo elements */
+		if ( parts[i].pos==-1 ) continue;
+
+		/* normal originInfo element */
+		if ( parts[i].code==0 ) {
+			output_fil( outptr, lvl2indent(incr_level(level,1)), parts[i].mods, f, parts[i].pos, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 		}
-		fprintf( outptr, "</%s>\n", origin[n].mods );
+
+		/* originInfo with placeTerm info */
+		else {
+			output_tag( outptr, lvl2indent(incr_level(level,1)), parts[i].mods, NULL,            TAG_OPEN,      TAG_NEWLINE, NULL );
+			output_fil( outptr, lvl2indent(incr_level(level,2)), "placeTerm",   f, parts[i].pos, TAG_OPENCLOSE, TAG_NEWLINE, "type", "text", NULL );
+			output_tag( outptr, lvl2indent(incr_level(level,1)), parts[i].mods, NULL,            TAG_CLOSE,     TAG_NEWLINE, NULL );
+		}
 	}
-	output_tab1( outptr, level, "</originInfo>\n" );
+
+	output_tag( outptr, lvl2indent(level), "originInfo", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
 }
 
+/* output_language_core()
+ *
+ *      generates language output for tag="langauge" or tag="languageOfCataloging"
+ *      if possible, outputs iso639-2b code for the language
+ *
+ * <language>
+ *     <languageTerm type="text">xxx</languageTerm>
+ * </language>
+ *
+ * <language>
+ *     <languageTerm type="text">xxx</languageTerm>
+ *     <languageTerm type="code" authority="iso639-2b">xxx</languageTerm>
+ * </language>
+ *
+ */
 static void
 output_language_core( fields *f, int n, FILE *outptr, char *tag, int level )
 {
-	newstr usetag;
 	char *lang, *code;
-	lang = fields_value( f, n, FIELDS_CHRP );
+
+	lang = (char *) fields_value( f, n, FIELDS_CHRP );
 	code = iso639_2_from_language( lang );
-	newstr_init( &usetag );
-	newstr_addchar( &usetag, '<' );
-	newstr_strcat( &usetag, tag );
-	newstr_strcat( &usetag, ">\n" );
-	output_tab1( outptr, level, usetag.data );
-	output_fill4( outptr, increment_level(level,1),
-		"languageTerm", "type", "text", f, n, 1 );
+
+	output_tag( outptr, lvl2indent(level),               tag,            NULL, TAG_OPEN,      TAG_NEWLINE, NULL );
+	output_tag( outptr, lvl2indent(incr_level(level,1)), "languageTerm", lang, TAG_OPENCLOSE, TAG_NEWLINE, "type", "text", NULL );
 	if ( code ) {
-		output_tab6( outptr, increment_level(level,1),
-			"languageTerm", "type", "code", "authority", "iso639-2b",
-			code, 1 );
+		output_tag( outptr, lvl2indent(incr_level(level,1)), "languageTerm", code, TAG_OPENCLOSE, TAG_NEWLINE, "type", "code", "authority", "iso639-2b", NULL );
 	}
-	newstr_strcpy( &usetag, "</" );
-	newstr_strcat( &usetag, tag );
-	newstr_strcat( &usetag, ">\n" );
-	output_tab1( outptr, level, usetag.data );
-	newstr_free( &usetag );
+	output_tag( outptr, lvl2indent(level),               tag,            NULL, TAG_CLOSE,     TAG_NEWLINE, NULL );
 }
 
 static void
@@ -463,19 +535,29 @@
 static void
 output_description( fields *f, FILE *outptr, int level )
 {
-	int n = fields_find( f, "DESCRIPTION", level );
+	char *val;
+	int n;
+
+	n = fields_find( f, "DESCRIPTION", level );
 	if ( n!=-1 ) {
-		output_tab1( outptr, level, "<physicalDescription>\n" );
-		output_fill2( outptr, increment_level(level,1), "note", f, n, 1 );
-		output_tab1( outptr, level, "</physicalDescription>\n" );
+		val = ( char * ) fields_value( f, n, FIELDS_CHRP );
+		output_tag( outptr, lvl2indent(level),               "physicalDescription", NULL, TAG_OPEN,      TAG_NEWLINE, NULL );
+		output_tag( outptr, lvl2indent(incr_level(level,1)), "note",                val,  TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+		output_tag( outptr, lvl2indent(level),               "physicalDescription", NULL, TAG_CLOSE,     TAG_NEWLINE, NULL );
 	}
 }
 
 static void
 output_toc( fields *f, FILE *outptr, int level )
 {
-	int n = fields_find( f, "CONTENTS", level );
-	output_fill2( outptr, level, "tableOfContents", f, n, 1 );
+	char *val;
+	int n;
+
+	n = fields_find( f, "CONTENTS", level );
+	if ( n!=-1 ) {
+		val = (char *) fields_value( f, n, FIELDS_CHRP );
+		output_tag( outptr, lvl2indent(level), "tableOfContents", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+	}
 }
 
 /* detail output
@@ -485,14 +567,13 @@
  * <detail type="volume"><number>xxx</number></detail
  */
 static void
-mods_output_detail( fields *f, FILE *outptr, int item, char *item_name,
-		int level )
+mods_output_detail( fields *f, FILE *outptr, int n, char *item_name, int level )
 {
-	if ( item==-1 ) return;
-	output_tab0( outptr, increment_level( level, 1 ) );
-	fprintf( outptr, "<detail type=\"%s\"><number>%s</number></detail>\n", 
-			item_name, f->data[item].data );
-	fields_setused( f, item );
+	if ( n!=-1 ) {
+		output_tag( outptr, lvl2indent(incr_level(level,1)), "detail", NULL,  TAG_OPEN,      TAG_NONEWLINE, "type", item_name, NULL );
+		output_fil( outptr, 0,                                "number", f, n,  TAG_OPENCLOSE, TAG_NONEWLINE, NULL );
+		output_tag( outptr, 0,                                "detail", NULL,  TAG_CLOSE,     TAG_NEWLINE,   NULL );                       
+	}
 }
 
 
@@ -504,27 +585,38 @@
  * </extent>
  */
 static void
-mods_output_extents( fields *f, FILE *outptr, int start, int end,
-		int total, char *extype, int level )
+mods_output_extents( fields *f, FILE *outptr, int start, int end, int total, char *extype, int level )
 {
-	output_tab0( outptr, increment_level(level,1) );
-	fprintf( outptr, "<extent unit=\"%s\">\n", extype);
-	output_fill2( outptr, increment_level(level,2), "start", f, start, 1 );
-	output_fill2( outptr, increment_level(level,2), "end", f, end, 1 );
-	output_fill2( outptr, increment_level(level,2), "total", f, total, 1 );
-	output_tab1 ( outptr, increment_level(level,1), "</extent>\n" );
+	char *val;
+
+	output_tag( outptr, lvl2indent(incr_level(level,1)), "extent", NULL, TAG_OPEN, TAG_NEWLINE, "unit", extype, NULL );
+	if ( start!=-1 ) {
+		val = (char *) fields_value( f, start, FIELDS_CHRP );
+		output_tag( outptr, lvl2indent(incr_level(level,2)), "start", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+	}
+	if ( end!=-1 ) {
+		val = (char *) fields_value( f, end, FIELDS_CHRP );
+		output_tag( outptr, lvl2indent(incr_level(level,2)), "end",   val, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+	}
+	if ( total!=-1 ) {
+		val = (char *) fields_value( f, total, FIELDS_CHRP );
+		output_tag( outptr, lvl2indent(incr_level(level,2)), "total", val, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+	}
+	output_tag( outptr, lvl2indent(incr_level(level,1)), "extent", NULL, TAG_CLOSE,     TAG_NEWLINE, NULL );
 }
 
 static void
 try_output_partheader( FILE *outptr, int wrote_header, int level )
 {
-	if ( !wrote_header ) output_tab1( outptr, level, "<part>\n" );
+	if ( !wrote_header )
+		output_tag( outptr, lvl2indent(level), "part", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
 }
 
 static void
 try_output_partfooter( FILE *outptr, int wrote_header, int level )
 {
-	if ( wrote_header ) output_tab1( outptr, level, "</part>\n" );
+	if ( wrote_header )
+		output_tag( outptr, lvl2indent(level), "part", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
 }
 
 /* part date output
@@ -535,33 +627,31 @@
 static int
 output_partdate( fields *f, FILE *outptr, int level, int wrote_header )
 {
-	convert parts[3] = {
-		{ "",	"PARTYEAR",                -1 },
-		{ "",	"PARTMONTH",               -1 },
-		{ "",	"PARTDAY",                 -1 },
+	convert parts[] = {
+		{ "",	"PARTDATE:YEAR",           0, 0 },
+		{ "",	"PARTDATE:MONTH",          0, 0 },
+		{ "",	"PARTDATE:DAY",            0, 0 },
 	};
 	int nparts = sizeof(parts)/sizeof(parts[0]);
 
-	if ( !find_alltags( f, parts, nparts, level ) ) return 0;
+	if ( !convert_findallfields( f, parts, nparts, level ) ) return 0;
 
 	try_output_partheader( outptr, wrote_header, level );
-	output_tab1( outptr, increment_level(level,1), "<date>" );
 
-	if ( parts[0].code!=-1 ) {
-		fprintf( outptr, "%s", f->data[ parts[0].code ].data);
-		fields_setused( f, parts[0].code );
+	output_tag( outptr, lvl2indent(incr_level(level,1)), "date", NULL, TAG_OPEN, TAG_NONEWLINE, NULL );
+
+	if ( parts[0].pos!=-1 ) {
+		fprintf( outptr, "%s", (char *) fields_value( f, parts[0].pos, FIELDS_CHRP ) );
 	} else fprintf( outptr, "XXXX" );
 
-	if ( parts[1].code!=-1 ) {
-		fprintf( outptr, "-%s", f->data[parts[1].code].data );
-		fields_setused( f, parts[1].code );
+	if ( parts[1].pos!=-1 ) {
+		fprintf( outptr, "-%s", (char *) fields_value( f, parts[1].pos, FIELDS_CHRP ) );
 	}
 
-	if ( parts[2].code!=-1 ) {
-		if ( parts[1].code!=-1 ) fprintf( outptr, "-" );
-		else fprintf( outptr, "-XX-" );
-		fprintf( outptr, "%s", f->data[parts[2].code].data );
-		fields_setused( f, parts[2].code );
+	if ( parts[2].pos!=-1 ) {
+		if ( parts[1].pos==-1 )
+			fprintf( outptr, "-XX" );
+		fprintf( outptr, "-%s", (char *) fields_value( f, parts[2].pos, FIELDS_CHRP ) );
 	}
 
 	fprintf( outptr,"</date>\n");
@@ -572,37 +662,32 @@
 static int
 output_partpages( fields *f, FILE *outptr, int level, int wrote_header )
 {
-	convert parts[4] = {
-		{ "",  "PAGESTART",                -1 },
-		{ "",  "PAGEEND",                  -1 },
-		{ "",  "PAGES",                    -1 },
-		{ "",  "TOTALPAGES",               -1 }
+	convert parts[] = {
+		{ "",  "PAGES:START",              0, 0 },
+		{ "",  "PAGES:STOP",               0, 0 },
+		{ "",  "PAGES",                    0, 0 },
+		{ "",  "PAGES:TOTAL",              0, 0 }
 	};
 	int nparts = sizeof(parts)/sizeof(parts[0]);
 
-	if ( !find_alltags( f, parts, nparts, level ) ) return 0;
+	if ( !convert_findallfields( f, parts, nparts, level ) ) return 0;
 
 	try_output_partheader( outptr, wrote_header, level );
 
-	/* If PAGESTART or PAGEEND are  undefined */
-	if ( parts[0].code==-1 || parts[1].code==-1 ) {
-		if ( parts[0].code!=-1 )
-			mods_output_detail( f, outptr, parts[0].code,
-				"page", level );
-		if ( parts[1].code!=-1 )
-			mods_output_detail( f, outptr, parts[1].code,
-				"page", level );
-		if ( parts[2].code!=-1 )
-			mods_output_detail( f, outptr, parts[2].code,
-				"page", level );
-		if ( parts[3].code!=-1 )
-			mods_output_extents( f, outptr, -1, -1,
-					parts[3].code, "page", level );
+	/* If PAGES:START or PAGES:STOP are undefined */
+	if ( parts[0].pos==-1 || parts[1].pos==-1 ) {
+		if ( parts[0].pos!=-1 )
+			mods_output_detail( f, outptr, parts[0].pos, "page", level );
+		if ( parts[1].pos!=-1 )
+			mods_output_detail( f, outptr, parts[1].pos, "page", level );
+		if ( parts[2].pos!=-1 )
+			mods_output_detail( f, outptr, parts[2].pos, "page", level );
+		if ( parts[3].pos!=-1 )
+			mods_output_extents( f, outptr, -1, -1, parts[3].pos, "page", level );
 	}
-	/* If both PAGESTART and PAGEEND are defined */
+	/* If both PAGES:START and PAGES:STOP are defined */
 	else {
-		mods_output_extents( f, outptr, parts[0].code, 
-			parts[1].code, parts[3].code, "page", level );
+		mods_output_extents( f, outptr, parts[0].pos, parts[1].pos, parts[3].pos, "page", level );
 	}
 
 	return 1;
@@ -612,34 +697,32 @@
 output_partelement( fields *f, FILE *outptr, int level, int wrote_header )
 {
 	convert parts[] = {
-		{ "volume",          "VOLUME",          -1 },
-		{ "section",         "SECTION",         -1 },
-		{ "issue",           "ISSUE",           -1 },
-		{ "number",          "NUMBER",          -1 },
-		{ "publiclawnumber", "PUBLICLAWNUMBER", -1 },
-		{ "session",         "SESSION",         -1 },
-		{ "articlenumber",   "ARTICLENUMBER",   -1 },
-		{ "part",            "PART",            -1 },
-		{ "chapter",         "CHAPTER",         -1 },
-		{ "report number",   "REPORTNUMBER",    -1 },
+		{ "",                "NUMVOLUMES",      0, 0 },
+		{ "volume",          "VOLUME",          0, 0 },
+		{ "section",         "SECTION",         0, 0 },
+		{ "issue",           "ISSUE",           0, 0 },
+		{ "number",          "NUMBER",          0, 0 },
+		{ "publiclawnumber", "PUBLICLAWNUMBER", 0, 0 },
+		{ "session",         "SESSION",         0, 0 },
+		{ "articlenumber",   "ARTICLENUMBER",   0, 0 },
+		{ "part",            "PART",            0, 0 },
+		{ "chapter",         "CHAPTER",         0, 0 },
+		{ "report number",   "REPORTNUMBER",    0, 0 },
 	};
-	int i, nparts = sizeof( parts ) / sizeof( convert ), n;
+	int i, nparts = sizeof( parts ) / sizeof( convert );
 
-	n = fields_find( f, "NUMVOLUMES", level );
-	if ( !find_alltags( f, parts, nparts, level ) && n==-1 ) return 0;
+	if ( !convert_findallfields( f, parts, nparts, level ) ) return 0;
+
 	try_output_partheader( outptr, wrote_header, level );
 
-	for ( i=0; i<nparts; ++i ) {
-		if ( parts[i].code==-1 ) continue;
-		mods_output_detail( f, outptr, parts[i].code, parts[i].mods,
-				level );
+	/* start loop at 1 to skip NUMVOLUMES */
+	for ( i=1; i<nparts; ++i ) {
+		if ( parts[i].pos==-1 ) continue;
+		mods_output_detail( f, outptr, parts[i].pos, parts[i].mods, level );
 	}
 
-	if ( n!=-1 ) {
-		output_tab1( outptr, level, "<extent unit=\"volumes\">\n" );
-		output_fill2( outptr, increment_level(level,1), "total", f, n, 1 );
-		output_tab1( outptr, level, "</extent>\n" );
-	}
+	if ( parts[0].pos!=-1 )
+		mods_output_extents( f, outptr, -1, -1, parts[0].pos, "volumes", level );
 
 	return 1;
 }
@@ -660,66 +743,78 @@
 	int n;
 	n = fields_find( f, "LANGCATALOG", level );
 	if ( n!=-1 ) {
-		output_tab1( outptr, level, "<recordInfo>\n" );
-		output_language_core( f, n, outptr, "languageOfCataloging", increment_level(level,1) );
-		output_tab1( outptr, level, "</recordInfo>\n" );
+		output_tag( outptr, lvl2indent(level), "recordInfo", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
+		output_language_core( f, n, outptr, "languageOfCataloging", incr_level(level,1) );
+		output_tag( outptr, lvl2indent(level), "recordInfo", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
 	}
 }
 
+/* output_genre()
+ *
+ * <genre authority="marcgt">thesis</genre>
+ * <genre>Diploma thesis</genre>
+ */
 static void
 output_genre( fields *f, FILE *outptr, int level )
 {
-	int i, ismarc, n;
-	char *value;
+	char *value, *attr, *attrvalue="marcgt";
+	int i, n;
+
 	n = fields_num( f );
 	for ( i=0; i<n; ++i ) {
 		if ( fields_level( f, i ) != level ) continue;
-		if ( !fields_match_tag( f, i, "GENRE" ) &&
-		     !fields_match_tag( f, i, "NGENRE" ) )
-			continue;
+		if ( !fields_match_tag( f, i, "GENRE" ) && !fields_match_tag( f, i, "NGENRE" ) ) continue;
 		value = fields_value( f, i, FIELDS_CHRP );
-		if ( marc_findgenre( value )!=-1 ) ismarc = 1;
-		else ismarc = 0;
-		output_tab1( outptr, level, "<genre" );
-		if ( ismarc ) 
-			fprintf( outptr, " authority=\"marcgt\"" );
-		fprintf( outptr, ">%s</genre>\n", value );
-		fields_setused( f, i );
+		attr = ( marc_findgenre( value ) == -1 ) ? NULL : "authority";
+		output_tag( outptr, lvl2indent(level), "genre", value, TAG_OPENCLOSE, TAG_NEWLINE, attr, attrvalue, NULL );
 	}
 }
 
+/* output_resource()
+ *
+ * <typeOfResource>text</typeOfResource>
+ */
 static void
-output_typeresource( fields *f, FILE *outptr, int level )
+output_resource( fields *f, FILE *outptr, int level )
 {
-	int n, ismarc = 0;
 	char *value;
+	int n;
+
 	n = fields_find( f, "RESOURCE", level );
 	if ( n!=-1 ) {
 		value = fields_value( f, n, FIELDS_CHRP );
-		if ( marc_findresource( value )!=-1 ) ismarc = 1;
-		if ( !ismarc ) {
-			fprintf( stderr, "Illegal typeofResource = '%s'\n", value );
+		if ( marc_findresource( value )!=-1 ) {
+			output_fil( outptr, lvl2indent(level), "typeOfResource", f, n, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 		} else {
-			output_fill2( outptr, level, "typeOfResource", f, n, 1 );
+			fprintf( stderr, "Illegal typeofResource = '%s'\n", value );
 		}
-		fields_setused( f, n );
 	}
 }
 
 static void
 output_type( fields *f, FILE *outptr, int level )
 {
-	int n = fields_find( f, "INTERNAL_TYPE", 0 );
+	int n;
+
+	/* silence warnings about INTERNAL_TYPE being unused */
+	n = fields_find( f, "INTERNAL_TYPE", 0 );
 	if ( n!=-1 ) fields_setused( f, n );
-	output_typeresource( f, outptr, level );
+
+	output_resource( f, outptr, level );
 	output_genre( f, outptr, level );
 }
 
+/* output_abs()
+ *
+ * <abstract>xxxx</abstract>
+ */
 static void
 output_abs( fields *f, FILE *outptr, int level )
 {
-	int nabs = fields_find( f, "ABSTRACT", level );
-	output_fill2( outptr, level, "abstract", f, nabs, 1 );
+	int n;
+
+	n = fields_find( f, "ABSTRACT", level );
+	output_fil( outptr, lvl2indent(level), "abstract", f, n, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 }
 
 static void
@@ -727,39 +822,52 @@
 {
 	int i, n;
 	char *t;
+
 	n = fields_num( f );
 	for ( i=0; i<n; ++i ) {
 		if ( fields_level( f, i ) != level ) continue;
 		t = fields_tag( f, i, FIELDS_CHRP_NOUSE );
 		if ( !strcasecmp( t, "NOTES" ) )
-			output_fill2( outptr, level, "note", f, i, 1 );
+			output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 		else if ( !strcasecmp( t, "PUBSTATE" ) )
-			output_fill4( outptr, level, "note", "type", "publication status", f, i, 1 );
+			output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "publication status", NULL );
 		else if ( !strcasecmp( t, "ANNOTE" ) )
-			output_fill2( outptr, level, "bibtex-annote", f, i, 1 );
+			output_fil( outptr, lvl2indent(level), "bibtex-annote", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 		else if ( !strcasecmp( t, "TIMESCITED" ) )
-			output_fill4( outptr, level, "note", "type", "times cited", f, i, 1 );
+			output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "times cited", NULL );
 		else if ( !strcasecmp( t, "ANNOTATION" ) )
-			output_fill4( outptr, level, "note", "type", "annotation", f, i, 1 );
+			output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "annotation", NULL );
 		else if ( !strcasecmp( t, "ADDENDUM" ) )
-			output_fill4( outptr, level, "note", "type", "addendum", f, i, 1 );
+			output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "addendum", NULL );
 		else if ( !strcasecmp( t, "BIBKEY" ) )
-			output_fill4( outptr, level, "note", "type", "bibliography key", f, i, 1 );
+			output_fil( outptr, lvl2indent(level), "note", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "bibliography key", NULL );
 	}
 }
 
+/* output_key()
+ *
+ * <subject>
+ *    <topic>xxxx</topic>
+ * </subject>
+ */
 static void
 output_key( fields *f, FILE *outptr, int level )
 {
 	int i, n;
+
 	n = fields_num( f );
 	for ( i=0; i<n; ++i ) {
 		if ( fields_level( f, i ) != level ) continue;
 		if ( !strcasecmp( f->tag[i].data, "KEYWORD" ) ) {
-			output_tab1( outptr, level, "<subject>\n" );
-			output_fill2( outptr, increment_level(level,1), "topic", f, i, 1 );
-			output_tab1( outptr, level, "</subject>\n" );
+			output_tag( outptr, lvl2indent(level),               "subject", NULL, TAG_OPEN,      TAG_NEWLINE, NULL );
+			output_fil( outptr, lvl2indent(incr_level(level,1)), "topic",   f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+			output_tag( outptr, lvl2indent(level),               "subject", NULL, TAG_CLOSE,     TAG_NEWLINE, NULL );
 		}
+		else if ( !strcasecmp( f->tag[i].data, "EPRINTCLASS" ) ) {
+			output_tag( outptr, lvl2indent(level),               "subject", NULL, TAG_OPEN,      TAG_NEWLINE, NULL );
+			output_fil( outptr, lvl2indent(incr_level(level,1)), "topic",   f, i, TAG_OPENCLOSE, TAG_NEWLINE, "class", "primary", NULL );
+			output_tag( outptr, lvl2indent(level),               "subject", NULL, TAG_CLOSE,     TAG_NEWLINE, NULL );
+		}
 	}
 }
 
@@ -767,89 +875,92 @@
 output_sn( fields *f, FILE *outptr, int level )
 {
 	convert sn_types[] = {
-		{ "isbn",      "ISBN",      0 },
-		{ "isbn",      "ISBN13",    0 },
-		{ "lccn",      "LCCN",      0 },
-		{ "issn",      "ISSN",      0 },
-		{ "citekey",   "REFNUM",    0 },
-		{ "doi",       "DOI",       0 },
-		{ "eid",       "EID",       0 },
-		{ "eprint",    "EPRINT",    0 },
-		{ "eprinttype","EPRINTTYPE",0 },
-		{ "pubmed",    "PMID",      0 },
-		{ "medline",   "MEDLINE",   0 },
-		{ "pii",       "PII",       0 },
-		{ "arXiv",     "ARXIV",     0 },
-		{ "isi",       "ISIREFNUM", 0 },
-		{ "accessnum", "ACCESSNUM", 0 },
-		{ "jstor",     "JSTOR",     0 },
-		{ "isrn",      "ISRN",      0 },
+		{ "isbn",      "ISBN",      0, 0 },
+		{ "isbn",      "ISBN13",    0, 0 },
+		{ "lccn",      "LCCN",      0, 0 },
+		{ "issn",      "ISSN",      0, 0 },
+		{ "coden",     "CODEN",     0, 0 },
+		{ "citekey",   "REFNUM",    0, 0 },
+		{ "doi",       "DOI",       0, 0 },
+		{ "eid",       "EID",       0, 0 },
+		{ "eprint",    "EPRINT",    0, 0 },
+		{ "eprinttype","EPRINTTYPE",0, 0 },
+		{ "pubmed",    "PMID",      0, 0 },
+		{ "MRnumber",  "MRNUMBER",  0, 0 },
+		{ "medline",   "MEDLINE",   0, 0 },
+		{ "pii",       "PII",       0, 0 },
+		{ "pmc",       "PMC",       0, 0 },
+		{ "arXiv",     "ARXIV",     0, 0 },
+		{ "isi",       "ISIREFNUM", 0, 0 },
+		{ "accessnum", "ACCESSNUM", 0, 0 },
+		{ "jstor",     "JSTOR",     0, 0 },
+		{ "isrn",      "ISRN",      0, 0 },
 	};
-	int n, ntypes = sizeof( sn_types ) / sizeof( sn_types[0] );
-	int found, i, nfields;
+	int ntypes = sizeof( sn_types ) / sizeof( sn_types[0] );
+	int i, n;
 
-	found = fields_find ( f, "CALLNUMBER", level );
-	output_fill2( outptr, level, "classification", f, found, 1 );
+	/* output call number */
+	n = fields_find( f, "CALLNUMBER", level );
+	output_fil( outptr, lvl2indent(level), "classification", f, n, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 
-	for ( n=0; n<ntypes; ++n ) {
-		found = fields_find( f, sn_types[n].internal, level );
-		if ( found==-1 ) continue;
-		output_tab0( outptr, level );
-		fprintf( outptr, "<identifier type=\"%s\">%s</identifier>\n",
-				sn_types[n].mods,
-				f->data[found].data
-		       );
-		fields_setused( f, found );
+	/* 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 );
 	}
-	nfields = fields_num( f );
-	for ( i=0; i<nfields; ++i ) {
+
+	/* output _all_ elements of type SERIALNUMBER */
+	n = fields_num( f );
+	for ( i=0; i<n; ++i ) {
 		if ( f->level[i]!=level ) continue;
-		if ( !strcasecmp( f->tag[i].data, "SERIALNUMBER" ) ) {
-			output_tab0( outptr, level );
-			fprintf( outptr, "<identifier type=\"%s\">%s</identifier>\n",
-				"serial number", f->data[i].data );
-			fields_setused( f, i );
-		}
+		if ( strcasecmp( f->tag[i].data, "SERIALNUMBER" ) ) continue;
+		output_fil( outptr, lvl2indent(level), "identifier", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "type", "serial number", NULL );
 	}
 }
 
+/* output_url()
+ *
+ * <location>
+ *     <url>URL</url>
+ *     <url urlType="pdf">PDFLINK</url>
+ *     <url displayLabel="Electronic full text" access="raw object">PDFLINK</url>
+ *     <physicalLocation>LOCATION</physicalLocation>
+ * </location>
+ */
 static void
 output_url( fields *f, FILE *outptr, int level )
 {
-	int location = fields_find( f, "LOCATION", level );
-	int url = fields_find( f, "URL", level );
+	int location   = fields_find( f, "LOCATION",   level );
+	int url        = fields_find( f, "URL",        level );
 	int fileattach = fields_find( f, "FILEATTACH", level );
-	int pdflink = fields_find( f, "PDFLINK", level );
+	int pdflink    = fields_find( f, "PDFLINK",    level );
 	int i, n;
+
 	if ( url==-1 && location==-1 && pdflink==-1 && fileattach==-1 ) return;
-	output_tab1( outptr, level, "<location>\n" );
+	output_tag( outptr, lvl2indent(level), "location", NULL, TAG_OPEN, TAG_NEWLINE, NULL );
+
 	n = fields_num( f );
 	for ( i=0; i<n; ++i ) {
 		if ( f->level[i]!=level ) continue;
-		if ( !strcasecmp( f->tag[i].data, "URL" ) ) {
-			output_fill2( outptr, increment_level(level,1), "url", f, i, 1 );
-		}
+		if ( strcasecmp( f->tag[i].data, "URL" ) ) continue;
+		output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 	}
 	for ( i=0; i<n; ++i ) {
 		if ( f->level[i]!=level ) continue;
-		if ( !strcasecmp( f->tag[i].data, "PDFLINK" ) ) {
-			output_fill2( outptr, increment_level(level,1), "url",
-				/*"urlType", "pdf",*/ f, i, 1 );
-		}
+		if ( strcasecmp( f->tag[i].data, "PDFLINK" ) ) continue;
+/*		output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "urlType", "pdf", NULL ); */
+		output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
 	}
 	for ( i=0; i<n; ++i ) {
 		if ( f->level[i]!=level ) continue;
-		if ( !strcasecmp( f->tag[i].data, "FILEATTACH" ) ){
-			output_tab0( outptr, increment_level(level,1) );
-			fprintf( outptr, "<url displayLabel=\"Electronic full text\" access=\"raw object\">" );
-			fprintf( outptr, "%s</url>\n", f->data[i].data );
-			fields_setused( f, i );
-		}
+		if ( strcasecmp( f->tag[i].data, "FILEATTACH" ) ) continue;
+		output_fil( outptr, lvl2indent(incr_level(level,1)), "url", f, i, TAG_OPENCLOSE, TAG_NEWLINE, "displayLabel", "Electronic full text", "access", "raw object", NULL );
 	}
 	if ( location!=-1 )
-		output_fill2( outptr, increment_level(level,1), "physicalLocation", f, 
-				location, 1 );
-	output_tab1( outptr, level, "</location>\n" );
+		output_fil( outptr, lvl2indent(incr_level(level,1)), "physicalLocation", f, location, TAG_OPENCLOSE, TAG_NEWLINE, NULL );
+
+	output_tag( outptr, lvl2indent(level), "location", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
 }
 
 /* refnum should start with a non-number and not include spaces -- ignore this */
@@ -913,20 +1024,16 @@
 	output_description( f, outptr, level );
 
 	if ( level >= 0 && level < max ) {
-		output_tab0( outptr, level );
-		fprintf( outptr, "<relatedItem type=\"host\">\n" );
-		output_citeparts( f, outptr, increment_level(level,1), max );
-		output_tab0( outptr, level );
-		fprintf( outptr, "</relatedItem>\n");
+		output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_OPEN,  TAG_NEWLINE, "type", "host", NULL );
+		output_citeparts( f, outptr, incr_level(level,1), max );
+		output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
 	}
 	/* Look for original item things */
 	orig_level = original_items( f, level );
 	if ( orig_level ) {
-		output_tab0( outptr, level );
-		fprintf( outptr, "<relatedItem type=\"original\">\n" );
+		output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_OPEN,  TAG_NEWLINE, "type", "original", NULL );
 		output_citeparts( f, outptr, orig_level, max );
-		output_tab0( outptr, level );
-		fprintf( outptr, "</relatedItem>\n" );
+		output_tag( outptr, lvl2indent(level), "relatedItem", NULL, TAG_CLOSE, TAG_NEWLINE, NULL );
 	}
 	output_abs(        f, outptr, level );
 	output_notes(      f, outptr, level );
@@ -957,7 +1064,7 @@
 		for ( i=0; i<n; ++i ) {
 			if ( fields_level( f, i ) != 0 ) continue;
 			tag = fields_tag( f, i, FIELDS_CHRP_NOUSE );
-			if ( strncasecmp( tag, "AUTHOR", 6 ) ) continue;
+			if ( strcasecmp( tag, "AUTHOR" ) && strcasecmp( tag, "AUTHOR:ASIS" ) && strcasecmp( tag, "AUTHOR:CORP" ) ) continue;
 			value = fields_value( f, i, FIELDS_CHRP_NOUSE );
 			if ( nwritten==0 ) fprintf( stderr, "\tAuthor(s) (level=0):\n" );
 			fprintf( stderr, "\t\t'%s'\n", value );
@@ -967,7 +1074,7 @@
 		for ( i=0; i<n; ++i ) {
 			if ( fields_level( f, i ) != 0 ) continue;
 			tag = fields_tag( f, i, FIELDS_CHRP_NOUSE );
-			if ( strcasecmp( tag, "YEAR" ) && strcasecmp( tag, "PARTYEAR" ) ) continue;
+			if ( strcasecmp( tag, "DATE:YEAR" ) && strcasecmp( tag, "PARTDATE:YEAR" ) ) continue;
 			value = fields_value( f, i, FIELDS_CHRP_NOUSE );
 			if ( nwritten==0 ) fprintf( stderr, "\tYear(s) (level=0):\n" );
 			fprintf( stderr, "\t\t'%s'\n", value );
@@ -996,12 +1103,12 @@
 	}
 }
 
-void
+static int
 modsout_write( fields *f, FILE *outptr, param *p, unsigned long numrefs )
 {
 	int max, dropkey;
 	max = fields_maxlevel( f );
-	dropkey = ( p->format_opts & MODSOUT_DROPKEY );
+	dropkey = ( p->format_opts & BIBL_FORMAT_MODSOUT_DROPKEY );
 
 	output_head( f, outptr, dropkey, numrefs );
 	output_citeparts( f, outptr, 0, max );
@@ -1009,9 +1116,11 @@
 
 	fprintf( outptr, "</mods>\n" );
 	fflush( outptr );
+
+	return BIBL_OK;
 }
 
-void
+static void
 modsout_writeheader( FILE *outptr, param *p )
 {
 	if ( p->utf8bom ) utf8_writebom( outptr );
@@ -1020,7 +1129,7 @@
 	fprintf(outptr,"<modsCollection xmlns=\"http://www.loc.gov/mods/v3\">\n");
 }
 
-void
+static void
 modsout_writefooter( FILE *outptr )
 {
 	fprintf(outptr,"</modsCollection>\n");
diff --git a/bibutils/modsout.h b/bibutils/modsout.h
deleted file mode 100644
--- a/bibutils/modsout.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * modsout.h
- *
- * Copyright (c) Chris Putnam 2003-2014
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef MODSOUT_H
-#define MODSOUT_H
-
-/* format-specific options */
-#define MODSOUT_DROPKEY (2)
-
-#include <stdio.h>
-#include <stdlib.h>
-#include "bibl.h"
-#include "bibutils.h"
-
-extern void modsout_writeheader( FILE *outptr, param *p );
-extern void modsout_writefooter( FILE *outptr );
-extern void modsout_write( fields *info, FILE *outptr,
-	param *p, unsigned long numrefs );
-extern void modsout_initparams( param *p, const char *progname );
-
-#endif
-
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-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *      
@@ -16,19 +16,20 @@
  *
  */
 convert identifier_types[] = {
-                { "citekey", "REFNUM" },
-                { "issn",    "ISSN"   },
-                { "isbn",    "ISBN"   },
-                { "doi",     "DOI"    },
-                { "url",     "URL"    },
-                { "uri",     "URL"    },
-                { "pubmed",  "PUBMED" },
-                { "medline", "MEDLINE" },
-                { "pii",     "PII" },
-                { "isi",     "ISIREFNUM" },
-		{ "lccn",    "LCCN" },
-                { "serial number", "SERIALNUMBER" },
-                { "accessnum", "ACCESSNUM" }
+	{ "citekey",       "REFNUM"    },
+	{ "issn",          "ISSN"      },
+	{ "isbn",          "ISBN"      },
+	{ "doi",           "DOI"       },
+	{ "url",           "URL"       },
+	{ "uri",           "URL"       },
+	{ "pubmed",        "PMID",     },
+	{ "medline",       "MEDLINE"   },
+	{ "pmc",           "PMC"       },
+	{ "pii",           "PII"       },
+	{ "isi",           "ISIREFNUM" },
+	{ "lccn",          "LCCN"      },
+	{ "serial number", "SERIALNUMBER" },
+	{ "accessnum",     "ACCESSNUM"    }
 };
 
 int nidentifier_types = sizeof( identifier_types ) / sizeof( identifier_types[0] );
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-2013
+ * Copyright (c) Chris Putnam 2008-2017
  *
  * Source code released under the GPL version 2
  *
@@ -9,8 +9,9 @@
 #ifndef MODSTYPES_H
 #define MODSTYPES_H
 typedef struct convert {
-        char *mods; /* old */
-        char *internal; /* new */
+	char *mods;     /* old */
+	char *internal; /* new */
+	int pos;
 	int code;
 } convert;
 
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-2014
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
@@ -14,9 +14,9 @@
 #include "utf8.h"
 #include "unicode.h"
 #include "is_ws.h"
-#include "newstr.h"
+#include "str.h"
 #include "fields.h"
-#include "list.h"
+#include "slist.h"
 #include "intlist.h"
 #include "name.h"
 
@@ -26,12 +26,12 @@
  * to 'family suffix, given given
  */
 void
-name_build_withcomma( newstr *s, char *p )
+name_build_withcomma( str *s, char *p )
 {
 	int nseps = 0, nch;
 	char *suffix, *stopat;
 
-	newstr_empty( s );
+	str_empty( s );
 
 	suffix = strstr( p, "||" );
 	if ( suffix ) stopat = suffix;
@@ -41,18 +41,18 @@
 		nch = 0;
 		if ( nseps==1 ) {
 			if ( suffix ) {
-				newstr_addchar( s, ' ' );
-				newstr_strcat( s, suffix+2 );
+				str_addchar( s, ' ' );
+				str_strcatc( s, suffix+2 );
 			}
-			newstr_addchar( s, ',' );
+			str_addchar( s, ',' );
 		}
-		if ( nseps ) newstr_addchar( s, ' ' );
+		if ( nseps ) str_addchar( s, ' ' );
 		while ( p!=stopat && *p!='|' ) {
-			newstr_addchar( s, *p++ );
+			str_addchar( s, *p++ );
 			nch++;
 		}
 		if ( p!=stopat && *p=='|' ) p++;
-		if ( nseps!=0 && nch==1 ) newstr_addchar( s, '.' );
+		if ( nseps!=0 && nch==1 ) str_addchar( s, '.' );
 		nseps++;
 	}
 }
@@ -63,14 +63,14 @@
  * of name lists.
  */
 int
-name_findetal( list *tokens )
+name_findetal( slist *tokens )
 {
-	newstr *s1, *s2;
+	str *s1, *s2;
 
 	if ( tokens->n==0 ) return 0;
 
 	/* ...check last entry for full 'et al.' or variant */
-	s2 = list_get( tokens, tokens->n - 1 );
+	s2 = slist_str( tokens, tokens->n - 1 );
 	if ( !strcasecmp( s2->data, "et alia" ) ||
 	     !strcasecmp( s2->data, "et al." )  ||
 	     !strcasecmp( s2->data, "et al.," )  ||
@@ -84,7 +84,7 @@
 	if ( tokens->n==1 ) return 0;
 
 	/* ...check last two entries for full 'et' and 'al.' */
-	s1 = list_get( tokens, tokens->n - 2 );
+	s1 = slist_str( tokens, tokens->n - 2 );
 	if ( !strcasecmp( s1->data, "et" ) ) {
 		if ( !strcasecmp( s2->data, "alia" ) ||
 		     !strcasecmp( s2->data, "al." )  ||
@@ -134,13 +134,13 @@
 }
 
 static int
-has_suffix( list *tokens, int begin, int end, int *suffixpos )
+has_suffix( slist *tokens, int begin, int end, int *suffixpos )
 {
 	int i, ret;
-	newstr *s;
+	str *s;
 
 	/* ...check last element, e.g. "H. F. Author, Sr." */
-	s = list_get( tokens, end - 1 );
+	s = slist_str( tokens, end - 1 );
 	ret = identify_suffix( s->data );
 	if ( ret ) {
 		*suffixpos = end - 1;
@@ -149,9 +149,9 @@
 
 	/* ...try to find one after a comma, e.g. "Author, Sr., H. F." */
 	for ( i=begin; i<end-1; ++i ) {
-		s = list_get( tokens, i );
+		s = slist_str( tokens, i );
 		if ( s->len && s->data[ s->len - 1 ]==',' ) {
-			s = list_get( tokens, i+1 );
+			s = slist_str( tokens, i+1 );
 			ret = identify_suffix( s->data );
 			if ( ret ) {
 				*suffixpos = i+1;
@@ -164,7 +164,7 @@
 }
 
 static int
-add_given_split( newstr *name, newstr *s )
+add_given_split( str *name, str *s )
 {
 	unsigned int unicode_char;
 	unsigned int pos = 0;
@@ -174,49 +174,49 @@
 		if ( is_ws( (char) unicode_char ) ) continue;
 		else if ( unicode_char==(unsigned int)'.' ) {
 			if ( s->data[pos]=='-' ) {
-				newstr_strcat( name, ".-" );
+				str_strcatc( name, ".-" );
 				pos += 1;
 				unicode_char = utf8_decode( s->data, &pos );
 				utf8_encode_str( unicode_char, utf8s );
-				newstr_strcat( name, utf8s );
-				newstr_addchar( name, '.' );
+				str_strcatc( name, utf8s );
+				str_addchar( name, '.' );
 			}
 		} else if ( unicode_char==(unsigned int)'-' ) {
-			newstr_strcat( name, ".-" );
+			str_strcatc( name, ".-" );
 			unicode_char = utf8_decode( s->data, &pos );
 			utf8_encode_str( unicode_char, utf8s );
-			newstr_strcat( name, utf8s );
-			newstr_addchar( name, '.' );
+			str_strcatc( name, utf8s );
+			str_addchar( name, '.' );
 		} else if ( unicode_char==(unsigned int)',' ) { /* nothing */
 		} else {
-			newstr_addchar( name, '|' );
+			str_addchar( name, '|' );
 			utf8_encode_str( unicode_char, utf8s );
-			newstr_strcat( name, utf8s );
+			str_strcatc( name, utf8s );
 		}
 	}
 	return 1;
 }
 
 static unsigned char
-token_has_no_upper( list *tokens, int n )
+token_has_no_upper( slist *tokens, int n )
 {
 	unsigned short m;
-	newstr *s;
-	s = list_get( tokens, n );
-	m = unicode_utf8_classify_newstr( s );
+	str *s;
+	s = slist_str( tokens, n );
+	m = unicode_utf8_classify_str( s );
 	if ( m & UNICODE_UPPER ) return 0;
 	else return 1;
 }
 
 static unsigned char
-token_has_upper( list *tokens, int n )
+token_has_upper( slist *tokens, int n )
 {
 	if ( token_has_no_upper( tokens, n ) ) return 0;
 	else return 1;
 }
 
 static int
-name_multielement_nocomma( intlist *given, intlist *family, list *tokens, int begin, int end, int suffixpos )
+name_multielement_nocomma( intlist *given, intlist *family, slist *tokens, int begin, int end, int suffixpos )
 {
 	int family_start, family_end;
 	int i, n;
@@ -253,9 +253,9 @@
 }
 
 static int
-name_multielement_comma( intlist *given, intlist *family, list *tokens, int begin, int end, int comma, int suffixpos )
+name_multielement_comma( intlist *given, intlist *family, slist *tokens, int begin, int end, int comma, int suffixpos )
 {
-	newstr *s;
+	str *s;
 	int i;
 
 	/* ...family names */
@@ -263,8 +263,8 @@
 		if ( i==suffixpos ) continue;
 		intlist_add( family, i );
 	}
-	s = list_get( tokens, comma );
-	newstr_trimend( s, 1 ); /* remove comma */
+	s = slist_str( tokens, comma );
+	str_trimend( s, 1 ); /* remove comma */
 	intlist_add( family, comma );
 
 	/* ...given names */
@@ -277,26 +277,26 @@
 }
 
 static int
-name_mutlielement_build( newstr *name, intlist *given, intlist *family, list *tokens )
+name_mutlielement_build( str *name, intlist *given, intlist *family, slist *tokens )
 {
 	unsigned short case_given = 0, case_family = 0, should_split = 0;
-	newstr *s;
+	str *s;
 	int i, m;
 
 	/* ...copy and analyze family name */
 	for ( i=0; i<family->n; ++i ) {
 		m = intlist_get( family, i );
-		s = list_get( tokens, m );
-		if ( i ) newstr_addchar( name, ' '  );
-		newstr_newstrcat( name, s );
-		case_family |= unicode_utf8_classify_newstr( s );
+		s = slist_str( tokens, m );
+		if ( i ) str_addchar( name, ' '  );
+		str_strcat( name, s );
+		case_family |= unicode_utf8_classify_str( s );
 	}
 
 	/* ...check given name case */
 	for ( i=0; i<given->n; ++i ) {
 		m = intlist_get( given, i );
-		s = list_get( tokens, m );
-		case_given |= unicode_utf8_classify_newstr( s );
+		s = slist_str( tokens, m );
+		case_given |= unicode_utf8_classify_str( s );
 	}
 
 	if ( ( ( case_family & UNICODE_MIXEDCASE ) == UNICODE_MIXEDCASE ) &&
@@ -306,35 +306,35 @@
 
 	for ( i=0; i<given->n; ++i ) {
 		m = intlist_get( given, i );
-		s = list_get( tokens, m );
+		s = slist_str( tokens, m );
 		if ( !should_split ) {
-			newstr_addchar( name, '|' );
-			newstr_newstrcat( name, s );
+			str_addchar( name, '|' );
+			str_strcat( name, s );
 		} else add_given_split( name, s );
 	}
 	return 1;
 }
 
 static int
-name_construct_multi( newstr *outname, list *tokens, int begin, int end )
+name_construct_multi( str *outname, slist *tokens, int begin, int end )
 {
 	int i, suffix, suffixpos=-1, comma=-1;
 	intlist given, family;
-	newstr *s;
+	str *s;
 
 	intlist_init( &family );
 	intlist_init( &given );
 
-	newstr_empty( outname );
+	str_empty( outname );
 
 	suffix = has_suffix( tokens, begin, end, &suffixpos );
 
 	for ( i=begin; i<end && comma==-1; i++ ) {
 		if ( i==suffixpos ) continue;
-		s = list_get( tokens, i );
+		s = slist_str( tokens, i );
 		if ( s->data[ s->len -1 ] == ',' ) {
 			if ( suffix && i==suffixpos-1 && !(suffix&WITHCOMMA) )
-				newstr_trimend( s, 1 );
+				str_trimend( s, 1 );
 			else
 				comma = i;
 		}
@@ -348,10 +348,10 @@
 	name_mutlielement_build( outname, &given, &family, tokens );
 
 	if ( suffix ) {
-		if ( suffix & JUNIOR ) newstr_strcat( outname, "||Jr." );
-		if ( suffix & SENIOR ) newstr_strcat( outname, "||Sr." );
-		if ( suffix & THIRD  ) newstr_strcat( outname, "||III" );
-		if ( suffix & FOURTH ) newstr_strcat( outname, "||IV"  );
+		if ( suffix & JUNIOR ) str_strcatc( outname, "||Jr." );
+		if ( suffix & SENIOR ) str_strcatc( outname, "||Sr." );
+		if ( suffix & THIRD  ) str_strcatc( outname, "||III" );
+		if ( suffix & FOURTH ) str_strcatc( outname, "||IV"  );
 	}
 
 	intlist_free( &given );
@@ -361,18 +361,18 @@
 }
 
 int
-name_addmultielement( fields *info, char *tag, list *tokens, int begin, int end, int level )
+name_addmultielement( fields *info, char *tag, slist *tokens, int begin, int end, int level )
 {
 	int status, ok = 1;
-	newstr name;
+	str name;
 
-	newstr_init( &name );
+	str_init( &name );
 
 	name_construct_multi( &name, tokens, begin, end );
-	status = fields_add( info, tag, name.data, level );
+	status = fields_add_can_dup( info, tag, name.data, level );
 	if ( status!=FIELDS_OK ) ok = 0;
 
-	newstr_free( &name );
+	str_free( &name );
 
 	return ok;
 }
@@ -388,14 +388,14 @@
 name_addsingleelement( fields *info, char *tag, char *name, int level, int corp )
 {
 	int status, ok = 1;
-	newstr outtag;
-	newstr_init( &outtag );
-	newstr_strcpy( &outtag, tag );
-	if ( !corp ) newstr_strcat( &outtag, ":ASIS" );
-	else newstr_strcat( &outtag, ":CORP" );
-	status = fields_add( info, outtag.data, name, level );
+	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;
-	newstr_free( &outtag );
+	str_free( &outtag );
 	return ok;
 }
 
@@ -409,31 +409,31 @@
  * Returns 3 on ok and name in corps list
  */
 int
-name_parse( newstr *outname, newstr *inname, list *asis, list *corps )
+name_parse( str *outname, str *inname, slist *asis, slist *corps )
 {
-	list tokens;
+	slist tokens;
 	int ret = 1;
 
-	newstr_empty( outname );
+	str_empty( outname );
 	if ( !inname || !inname->len ) return ret;
 
-	list_init( &tokens );
+	slist_init( &tokens );
 
-	if ( asis && list_find( asis, inname->data ) !=-1 ) {
-		newstr_newstrcpy( outname, inname );
+	if ( asis && slist_find( asis, inname ) !=-1 ) {
+		str_strcpy( outname, inname );
 		ret = 2;
 		goto out;
-	} else if ( corps && list_find( corps, inname->data ) != -1 ) {
-		newstr_newstrcpy( outname, inname );
+	} else if ( corps && slist_find( corps, inname ) != -1 ) {
+		str_strcpy( outname, inname );
 		ret = 3;
 		goto out;
 	}
 
-	newstr_findreplace( inname, ",", ", " );
-	list_tokenize( &tokens, inname, " ", 1 );
+	str_findreplace( inname, ",", ", " );
+	slist_tokenize( &tokens, inname, " ", 1 );
 
 	if ( tokens.n==1 ) {
-		newstr_newstrcpy( outname, inname );
+		str_strcpy( outname, inname );
 		ret = 2;
 	} else {
 		name_construct_multi( outname, &tokens, 0, tokens.n );
@@ -442,17 +442,17 @@
 
 out:
 
-	list_free( &tokens );
+	slist_free( &tokens );
 
 	return ret;
 }
 
 static char *
-name_copy( newstr *name, char *p )
+name_copy( str *name, char *p )
 {
 	char *start, *end, *q;
 
-	newstr_empty( name );
+	str_empty( name );
 
 	start = p = skip_ws( p );
 
@@ -465,7 +465,7 @@
 	if ( *p=='|' ) p++;
 
 	for ( q=start; q<=end; q++ )
-		newstr_addchar( name, *q );
+		str_addchar( name, *q );
 
 	return p;
 }
@@ -486,16 +486,16 @@
  * "Author, H. F."
  */
 int
-name_add( fields *info, char *tag, char *q, int level, list *asis, list *corps )
+name_add( fields *info, char *tag, char *q, int level, slist *asis, slist *corps )
 {
 	int ok, status, nametype, ret = 1;
-	newstr inname, outname;
-	list tokens;
+	str inname, outname;
+	slist tokens;
 
 	if ( !q ) return 0;
 
-	list_init( &tokens );
-	newstrs_init( &inname, &outname, NULL );
+	slist_init( &tokens );
+	strs_init( &inname, &outname, NULL );
 
 	while ( *q ) {
 
@@ -505,7 +505,7 @@
 		if ( !nametype ) { ret = 0; goto out; }
 
 		if ( nametype==1 ) {
-			status = fields_add( info, tag, outname.data, level );
+			status = fields_add_can_dup( info, tag, outname.data, level );
 			ok = ( status==FIELDS_OK ) ? 1 : 0;
 		}
 		else if ( nametype==2 )
@@ -518,8 +518,8 @@
 	}
 
 out:
-	newstrs_free( &inname, &outname, NULL );
-	list_free( &tokens );
+	strs_free( &inname, &outname, NULL );
+	slist_free( &tokens );
 
 	return ret;
 }
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-2014
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
@@ -11,16 +11,16 @@
 #ifndef NAME_H
 #define NAME_H
 
-#include "newstr.h"
-#include "list.h"
+#include "str.h"
+#include "slist.h"
 #include "fields.h"
 
-extern int  name_add( fields *info, char *tag, char *q, int level, list *asis, list *corps );
-extern void name_build_withcomma( newstr *s, char *p );
-extern int  name_parse( newstr *outname, newstr *inname, list *asis, list *corps );
+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, list *tokens, int begin, int end, int level );
-extern int  name_findetal( list *tokens );
+extern int  name_addmultielement( fields *info, char *tag, slist *tokens, int begin, int end, int level );
+extern int  name_findetal( slist *tokens );
 
 #endif
 
diff --git a/bibutils/nbibin.c b/bibutils/nbibin.c
new file mode 100644
--- /dev/null
+++ b/bibutils/nbibin.c
@@ -0,0 +1,505 @@
+/*
+ * nbibin.c
+ *
+ * Copyright (c) Chris Putnam 2016-2017
+ *
+ * Source code released under the GPL version 2
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "is_ws.h"
+#include "str.h"
+#include "str_conv.h"
+#include "fields.h"
+#include "name.h"
+#include "title.h"
+#include "url.h"
+#include "serialno.h"
+#include "reftypes.h"
+#include "bibformats.h"
+#include "generic.h"
+
+extern variants nbib_all[];
+extern int nbib_nall;
+
+/*****************************************************
+ PUBLIC: void nbib_initparams()
+*****************************************************/
+
+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_convertf( fields *nbib, fields *info, int reftype, param *p );
+
+void
+nbibin_initparams( param *p, 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;
+
+	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;
+
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
+
+	if ( !progname ) p->progname = NULL;
+	else p->progname = strdup( progname );
+}
+
+/*****************************************************
+ PUBLIC: int nbib_readf()
+*****************************************************/
+
+/* RIS definition of a tag is strict:
+    character 1 = uppercase alphabetic character
+    character 2 = uppercase alphabetic character
+    character 3 = character or space (ansi 32)
+    character 4 = character or space (ansi 32)
+    character 5 = dash (ansi 45)
+    character 6 = space (ansi 32)
+*/
+static int
+is_upperchar( char c )
+{
+	if ( c>='A' && c<='Z' ) return 1;
+	else return 0;
+}
+
+static int
+is_upperchar_space( char c )
+{
+	if ( c==' ' ) return 1;
+	if ( c>='A' && c<='Z' ) return 1;
+	else return 0;
+}
+
+static int
+nbib_istag( char *buf )
+{
+	if ( !is_upperchar( buf[0] ) ) return 0;
+	if ( !is_upperchar( buf[1] ) ) return 0;
+	if ( !is_upperchar_space( buf[2] ) ) return 0;
+	if ( !is_upperchar_space( buf[3] ) ) return 0;
+	if (buf[4]!='-') return 0;
+	if (buf[5]!=' ') return 0;
+	return 1;
+}
+
+static int
+readmore( FILE *fp, char *buf, int bufsize, int *bufpos, str *line )
+{
+	if ( line->len ) return 1;
+	else return str_fget( fp, buf, bufsize, bufpos, line );
+}
+
+static int
+skip_utf8_bom( str *line, int *fcharset )
+{
+	unsigned char *up;
+
+	if ( line->len < 3 ) return 0;
+
+	up = ( unsigned char *) str_cstr( line );
+	if ( up[0]==0xEF && up[1]==0xBB && up[2]==0xBF ) {
+		*fcharset = CHARSET_UNICODE;
+		return 3;
+	}
+
+	return 0;
+}
+
+static int
+nbib_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
+{
+	int n, haveref = 0, inref = 0, readtoofar = 0;
+	char *p;
+
+	*fcharset = CHARSET_UNKNOWN;
+
+	while ( !haveref && readmore( fp, buf, bufsize, bufpos, line ) ) {
+
+		/* ...references are terminated by an empty line */
+		if ( !line->data || line->len==0 ) {
+			if ( reference->len ) haveref = 1;
+			continue;
+		}
+
+		/* ...recognize and skip over UTF8 BOM */
+		n = skip_utf8_bom( line, fcharset );
+		p = &( line->data[n] );
+
+		/* Each reference starts with 'PMID- ' && ends with blank line */
+		if ( strncmp(p,"PMID- ",6)==0 ) {
+			if ( !inref ) {
+				inref = 1;
+			} else {
+				/* we've read too far.... */
+				readtoofar = 1;
+				inref = 0;
+			}
+		}
+		if ( nbib_istag( p ) ) {
+			if ( !inref ) {
+				fprintf(stderr,"Warning.  Tagged line not "
+					"in properly started reference.\n");
+				fprintf(stderr,"Ignored: '%s'\n", p );
+			} else if ( !strncmp(p,"ER  -",5) ) {
+				inref = 0;
+			} else {
+				str_addchar( reference, '\n' );
+				str_strcatc( reference, p );
+			}
+		}
+		/* not a tag, but we'll append to last values ...*/
+		else if ( inref && strlen( p ) >= 6 ) {
+			str_strcatc( reference, p+5 );
+		}
+		if ( !readtoofar ) str_empty( line );
+	}
+	if ( inref ) haveref = 1;
+	return haveref;
+}
+
+/*****************************************************
+ PUBLIC: int nbib_processf()
+*****************************************************/
+
+static char*
+process_line2( str *tag, str *data, char *p )
+{
+	while ( *p==' ' || *p=='\t' ) p++;
+	while ( *p && *p!='\r' && *p!='\n' )
+		str_addchar( data, *p++ );
+	while ( *p=='\r' || *p=='\n' ) p++;
+	return p;
+}
+
+static char*
+process_line( str *tag, str *data, char *p )
+{
+	int i;
+
+	i = 0;
+	while ( i<6 && *p ) {
+		if ( *p!=' ' && *p!='-' ) str_addchar( tag, *p );
+		p++;
+		i++;
+	}
+	while ( *p==' ' || *p=='\t' ) p++;
+	while ( *p && *p!='\r' && *p!='\n' )
+		str_addchar( data, *p++ );
+	str_trimendingws( data );
+	while ( *p=='\n' || *p=='\r' ) p++;
+	return p;
+}
+
+static int
+nbib_processf( fields *nbib, char *p, char *filename, long nref, param *pm )
+{
+	str tag, data;
+	int status, n;
+
+	strs_init( &tag, &data, NULL );
+
+	while ( *p ) {
+		if ( nbib_istag( p ) )
+			p = process_line( &tag, &data, p );
+		/* no anonymous fields allowed */
+		if ( str_has_value( &tag ) ) {
+			status = fields_add( nbib, str_cstr( &tag ), str_cstr( &data ), 0 );
+			if ( status!=FIELDS_OK ) return 0;
+		} else {
+			p = process_line2( &tag, &data, p );
+			n = fields_num( nbib );
+			if ( data.len && n>0 ) {
+				str *od;
+				od = fields_value( nbib, n-1, FIELDS_STRP );
+				str_addchar( od, ' ' );
+				str_strcat( od, &data );
+			}
+		}
+		strs_empty( &tag, &data, NULL );
+	}
+
+	strs_free( &tag, &data, NULL );
+	return 1;
+}
+
+/*****************************************************
+ PUBLIC: int nbib_typef()
+*****************************************************/
+
+/*
+ * PT  - Case Reports
+ * PT  - Journal Article
+ * PT  - Research Support, N.I.H., Extramural
+ * PT  - Review
+ */
+static int
+nbib_typef( fields *nbib, char *filename, int nref, param *p )
+{
+	int reftype, nrefname, is_default;
+	char *typename, *refname = "";
+	vplist_index i;
+	vplist a;
+
+	nrefname  = fields_find( nbib, "PMID", LEVEL_MAIN );
+	if ( nrefname!=-1 ) refname = fields_value( nbib, nrefname, FIELDS_CHRP_NOUSE );
+
+	vplist_init( &a );
+
+	fields_findv_each( nbib, LEVEL_MAIN, FIELDS_CHRP_NOUSE, &a, "PT" );
+	is_default = 1;
+	for ( i=0; i<a.n; ++i ) {
+		typename = vplist_get( &a, i );
+		reftype  = get_reftype( typename, nref, p->progname, p->all, p->nall, refname, &is_default, REFTYPE_SILENT );
+		if ( !is_default ) break;
+	}
+
+	if ( a.n==0 )
+		reftype = get_reftype( "", nref, p->progname, p->all, p->nall, refname, &is_default, REFTYPE_CHATTY );
+	else if ( is_default ) {
+                if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
+                fprintf( stderr, "Did not recognize type of refnum %d (%s).\n"
+                        "\tDefaulting to %s.\n", nref, refname, p->all[0].type );
+	}
+
+	vplist_free( &a );
+
+	return reftype;
+}
+
+/*****************************************************
+ PUBLIC: int nbib_convertf()
+*****************************************************/
+
+/* PB  - 2016 May 7 */
+
+static int
+nbibin_date( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int fstatus, status = BIBL_OK;
+	str s;
+	char *p;
+
+	p = str_cstr( invalue );
+	if ( !p ) return status;
+
+	str_init( &s );
+
+	/* ...handle year */
+		while ( *p && !is_ws( *p ) ) {
+			str_addchar( &s, *p );
+			p++;
+		}
+
+		if ( str_memerr( &s ) ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+
+		if ( str_has_value( &s ) ) {
+			fstatus = fields_add( bibout, "DATE:YEAR", str_cstr( &s ), LEVEL_MAIN );
+			if ( fstatus!=FIELDS_OK ) {
+				status = BIBL_ERR_MEMERR;
+				goto out;
+			}
+		}
+
+	/* ...handle month */
+		str_empty( &s );
+		while ( is_ws( *p ) ) p++;
+		while ( *p && !is_ws( *p ) ) {
+			str_addchar( &s, *p );
+			p++;
+		}
+
+		if ( str_memerr( &s ) ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+
+		if ( str_has_value( &s ) ) {
+			fstatus = fields_add( bibout, "DATE:MONTH", str_cstr( &s ), LEVEL_MAIN );
+			if ( fstatus!=FIELDS_OK ) {
+				status = BIBL_ERR_MEMERR;
+				goto out;
+			}
+		}
+
+	/* ...handle day */
+		str_empty( &s );
+		while ( is_ws( *p ) ) p++;
+		while ( *p && !is_ws( *p ) ) {
+			str_addchar( &s, *p );
+			p++;
+		}
+
+		if ( str_memerr( &s ) ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+
+		if ( str_has_value( &s ) ) {
+			fstatus = fields_add( bibout, "DATE:DAY", str_cstr( &s ), LEVEL_MAIN );
+			if ( fstatus!=FIELDS_OK ) {
+				status = BIBL_ERR_MEMERR;
+				goto out;
+			}
+		}
+
+out:
+	str_free( &s );
+
+	return status;
+}
+
+/* the LID and AID fields that can be doi's or pii's */
+static int
+nbibin_doi( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int fstatus, sstatus, status = BIBL_OK;
+	char *id, *type, *usetag="";
+	slist tokens;
+
+	slist_init( &tokens );
+
+	sstatus = slist_tokenize( &tokens, invalue, " ", 1 );
+	if ( sstatus!=SLIST_OK ) {
+		status = BIBL_ERR_MEMERR;
+		goto out;
+	}
+
+	if ( tokens.n == 2 ) {
+		id   = slist_cstr( &tokens, 0 );
+		type = slist_cstr( &tokens, 1 );
+		if ( !strcmp( type, "[doi]" ) ) usetag = "DOI";
+		else if ( !strcmp( type, "[pii]" ) ) usetag = "PII";
+		if ( strlen( outtag ) > 0 ) {
+			fstatus = fields_add( bibout, usetag, id, LEVEL_MAIN );
+			if ( fstatus!=FIELDS_OK ) {
+				status = BIBL_ERR_MEMERR;
+				goto out;
+			}
+		}
+	}
+out:
+	slist_free( &tokens );
+	return status;
+}
+
+static int
+nbibin_pages( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
+{
+	int fstatus, status = BIBL_OK;
+	str sp, tmp, ep;
+	char *p;
+	int i;
+
+	p = str_cstr( invalue );
+	if ( !p ) return BIBL_OK;
+
+	strs_init( &sp, &tmp, &ep, NULL );
+
+	while ( *p && *p!='-' ) {
+		str_addchar( &sp, *p );
+		p++;
+	}
+
+	if ( str_memerr( &sp ) ) {
+		status = BIBL_ERR_MEMERR;
+		goto out;
+	}
+
+	while ( *p=='-' ) p++;
+
+	while ( *p ) {
+		str_addchar( &tmp, *p );
+		p++;
+	}
+
+	if ( sp.len ) {
+		fstatus = fields_add( bibout, "PAGES:START", str_cstr( &sp ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+	}
+
+	if ( tmp.len ) {
+		for ( i=0; i<sp.len - tmp.len; ++i )
+			str_addchar( &ep, sp.data[i] );
+		str_strcat( &ep, &tmp );
+
+		fstatus = fields_add( bibout, "PAGES:STOP", str_cstr( &ep ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+	}
+out:
+	strs_free( &sp, &tmp, &ep, NULL );
+	return status;
+}
+
+static void
+nbib_report_notag( param *p, char *tag )
+{
+	if ( p->verbose && strcmp( tag, "TY" ) ) {
+		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
+		fprintf( stderr, "Did not identify NBIB tag '%s'\n", tag );
+	}
+}
+
+static int
+nbib_convertf( fields *bibin, fields *bibout, int reftype, param *p )
+{
+	static int (*convertfns[NUM_REFTYPES])(fields *, int i, str *, str *, int, param *, char *, fields *) = {
+		[ 0 ... NUM_REFTYPES-1 ] = generic_null,
+		[ SIMPLE       ] = generic_simple,
+		[ TITLE        ] = generic_title,
+		[ PERSON       ] = generic_person,
+		[ SKIP         ] = generic_skip,
+		[ DATE         ] = nbibin_date,
+		[ PAGES        ] = nbibin_pages,
+		[ DOI          ] = nbibin_doi,
+        };
+	int process, level, i, nfields, status = BIBL_OK;
+	str *intag, *invalue;
+	char *outtag;
+
+	nfields = fields_num( bibin );
+
+	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 );
+			continue;
+		}
+		invalue = fields_value( bibin, i, FIELDS_STRP );
+
+		status = convertfns[ process ] ( bibin, i, intag, invalue, level, p, outtag, bibout );
+		if ( status!=BIBL_OK ) return status;
+	}
+
+	if ( status==BIBL_OK && p->verbose ) fields_report( bibout, stderr );
+
+	return status;
+}
diff --git a/bibutils/nbibtypes.c b/bibutils/nbibtypes.c
new file mode 100644
--- /dev/null
+++ b/bibutils/nbibtypes.c
@@ -0,0 +1,68 @@
+/*
+ * nbibtypes.c
+ *
+ * Copyright (c) Chris Putnam 2016-2017
+ *
+ * Program and source code released under the GPL version 2
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include "is_ws.h"
+#include "fields.h"
+#include "reftypes.h"
+
+static lookups article[] = {
+	{ "PMID",   "PMID",               SIMPLE, LEVEL_MAIN },
+	{ "OWN",    "",                   SKIP,   LEVEL_MAIN },
+	{ "STAT",   "",                   SKIP,   LEVEL_MAIN },
+	{ "DA",     "",                   SKIP,   LEVEL_MAIN },
+	{ "DCOM",   "",                   SKIP,   LEVEL_MAIN },
+	{ "LR",     "",                   SKIP,   LEVEL_MAIN },
+	{ "IS",     "",                   SKIP,   LEVEL_MAIN },
+	{ "VI",     "VOLUME",             SIMPLE, LEVEL_MAIN },
+	{ "IP",     "ISSUE",              SIMPLE, LEVEL_MAIN },
+	{ "DP",     "",                   DATE,   LEVEL_MAIN }, /* date published? */
+	{ "TI",     "TITLE",              TITLE,  LEVEL_MAIN },
+	{ "PG",     "PAGES",              PAGES,  LEVEL_MAIN },
+	{ "LID",    "DOI",                DOI,    LEVEL_MAIN }, /* linking ID? -- can be DOI/PII */
+	{ "AB",     "ABSTRACT",           SIMPLE, LEVEL_MAIN },
+	{ "FAU",    "AUTHOR",             PERSON, LEVEL_MAIN },
+	{ "AU",     "",                   SKIP,   LEVEL_MAIN },
+	{ "AD",     "",                   SKIP,   LEVEL_MAIN },
+	{ "LA",     "",                   SKIP,   LEVEL_MAIN },
+	{ "GR",     "",                   SKIP,   LEVEL_MAIN },
+	{ "DEP",    "",                   SKIP,   LEVEL_MAIN }, /* a date */
+	{ "PL",     "ADDRESS",            SIMPLE, LEVEL_MAIN }, /* Publisher location */
+	{ "TA",     "SHORTTITLE",         SIMPLE, LEVEL_HOST }, /* Journal title abbreviation */
+	{ "JT",     "TITLE",              SIMPLE, LEVEL_HOST }, /* Journal title */
+	{ "JID",    "",                   SKIP,   LEVEL_HOST }, /* Journal ID? */
+	{ "SB",     "",                   SKIP,   LEVEL_MAIN },
+	{ "PMC",    "PMC",                SIMPLE, LEVEL_MAIN },
+	{ "OID",    "",                   SKIP,   LEVEL_MAIN },
+	{ "EDAT",   "",                   SKIP,   LEVEL_MAIN },
+	{ "MHDA",   "",                   SKIP,   LEVEL_MAIN },
+	{ "CRDT",   "",                   SKIP,   LEVEL_MAIN },
+	{ "PHST",   "",                   SKIP,   LEVEL_MAIN }, /* Publication history? */
+	{ "AID",    "DOI",                DOI,    LEVEL_MAIN }, /* Article ID? -- can be DOI/PII */
+	{ "PST",    "",                   SKIP,   LEVEL_MAIN },
+	{ "SO",     "",                   SKIP,   LEVEL_MAIN },
+	{ " ",      "INTERNAL_TYPE|ARTICLE",  ALWAYS, LEVEL_MAIN },
+	{ " ",      "ISSUANCE|continuing",    ALWAYS, LEVEL_HOST },
+	{ " ",      "RESOURCE|text",          ALWAYS, LEVEL_MAIN },
+	{ " ",      "GENRE|journal article",  ALWAYS, LEVEL_MAIN },
+	{ " ",      "GENRE|periodical",       ALWAYS, LEVEL_HOST },
+	{ " ",      "GENRE|academic journal", ALWAYS, LEVEL_HOST }
+};
+
+#define ORIG(a) ( &(a[0]) )
+#define SIZE(a) ( sizeof( a ) / sizeof( lookups ) )
+#define REFTYPE(a,b) { a, ORIG(b), SIZE(b) }
+
+variants nbib_all[] = {
+	REFTYPE( "Journal article", article ),
+	REFTYPE( "News",            article ),
+};
+
+int nbib_nall = sizeof( nbib_all ) / sizeof( variants );
+
diff --git a/bibutils/newstr.c b/bibutils/newstr.c
deleted file mode 100644
--- a/bibutils/newstr.c
+++ /dev/null
@@ -1,901 +0,0 @@
-/*
- * newstr.c
- *
- * Version: 04/27/14
- *
- * Copyright (c) Chris Putnam 1999-2014
- *
- * Source code released under the GPL version 2
- *
- *
- * routines for dynamically allocated strings
- *
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <ctype.h>
-#include <limits.h>
-#include "newstr.h"
-#include "is_ws.h"
-
-/* Do not use asserts in NEWSTR_NOASSERT defined */
-#ifdef NEWSTR_NOASSERT
-#define NDEBUG
-#endif
-#include <assert.h>
-
-#define newstr_initlen (64)
-
-
-/* Clear memory in resize/free if NEWSTR_PARANOIA defined */
-
-#ifndef NEWSTR_PARANOIA
-
-static void 
-newstr_realloc( newstr *s, unsigned long minsize )
-{
-	char *newptr;
-	unsigned long size;
-	assert( s );
-	size = 2 * s->dim;
-	if (size < minsize) size = minsize;
-	newptr = (char *) realloc( s->data, sizeof( *(s->data) )*size );
-	if ( !newptr ) {
-		fprintf(stderr,"Error.  Cannot reallocate memory (%ld bytes) in newstr_realloc.\n", sizeof(*(s->data))*size);
-		exit( EXIT_FAILURE );
-	}
-	s->data = newptr;
-	s->dim = size;
-}
-
-/* define as a no-op */
-#define newstr_nullify( s )
-
-#else
-
-static void 
-newstr_realloc( newstr *s, unsigned long minsize )
-{
-	char *newptr;
-	unsigned long size;
-	assert( s );
-	size = 2 * s->dim;
-	if ( size < minsize ) size = minsize;
-	newptr = (char *) malloc( sizeof( *(s->data) ) * size );
-	if ( !newptr ) {
-		fprintf( stderr, "Error.  Cannot reallocate memory (%d bytes)"
-			" in newstr_realloc.\n", sizeof(*(s->data))*size );
-		exit( EXIT_FAILURE );
-	}
-	if ( s->data ) {
-		newstr_nullify( s );
-		free( s->data );
-	}
-	s->data = newptr;
-	s->dim = size;
-}
-
-static inline void
-newstr_nullify( newstr *s )
-{
-	memset( s->data, 0, s->dim );
-}
-
-#endif
-
-void 
-newstr_init( newstr *s )
-{
-	assert( s );
-	s->dim = 0;
-	s->len = 0;
-	s->data = NULL;
-}
-
-void
-newstr_initstr( newstr *s, const char *initstr )
-{
-	assert( s );
-	assert( initstr );
-	newstr_init( s );
-	newstr_strcpy( s, initstr );
-}
-
-void
-newstrs_init( newstr *s, ... )
-{
-	newstr *s2;
-	va_list ap;
-	newstr_init( s );
-	va_start( ap, s );
-	do {
-		s2 = va_arg( ap, newstr * );
-		if ( s2 ) newstr_init( s2 );
-	} while ( s2 );
-	va_end( ap );
-}
-
-/*
- * This is currently a stub. Later it will
- * report whether or not a newstr function
- * could not be performed due to a memory
- * error.
- */
-int
-newstr_memerr( newstr *s )
-{
-	return 0;
-}
-
-void
-newstr_mergestrs( newstr *s, ... )
-{
-	va_list ap;
-	const char *cp;
-	newstr_empty( s );
-	va_start( ap, s );
-	do {
-		cp = va_arg( ap, const char * );
-		if ( cp ) newstr_strcat( s, cp );
-	} while ( cp );
-	va_end( ap );
-}
-
-static void 
-newstr_initalloc( newstr *s, unsigned long minsize )
-{
-	unsigned long size = newstr_initlen;
-	assert( s );
-	if ( minsize > newstr_initlen ) size = minsize;
-	s->data = (char *) malloc (sizeof( *(s->data) ) * size);
-	if ( !s->data ) {
-		fprintf(stderr,"Error.  Cannot allocate memory in newstr_initalloc.\n");
-		exit( EXIT_FAILURE );
-	}
-	s->data[0]='\0';
-	s->dim=size;
-	s->len=0;
-}
-
-newstr *
-newstr_new( void )
-{
-	newstr *s = (newstr *) malloc( sizeof( *s ) );
-	if ( s )
-		newstr_initalloc( s, newstr_initlen );
-	return s;
-}
-
-void 
-newstr_free( newstr *s )
-{
-	assert( s );
-	if ( s->data ) {
-		newstr_nullify( s );
-		free( s->data );
-	}
-	s->dim = 0;
-	s->len = 0;
-	s->data = NULL;
-}
-
-void
-newstrs_free( newstr *s, ... )
-{
-	newstr *s2;
-	va_list ap;
-	newstr_free( s );
-	va_start( ap, s );
-	do {
-		s2 = va_arg( ap, newstr * );
-		if ( s2 ) newstr_free( s2 );
-	} while ( s2 );
-	va_end( ap );
-}
-
-void
-newstr_delete( newstr *s )
-{
-	assert( s );
-	newstr_free( s );
-	free( s );
-}
-
-void
-newstr_empty( newstr *s )
-{
-	assert( s );
-	if ( s->data ) {
-		newstr_nullify( s );
-		s->data[0] = '\0';
-	}
-	s->len = 0;
-}
-
-void
-newstrs_empty( newstr *s, ... )
-{
-	newstr *s2;
-	va_list ap;
-	newstr_empty( s );
-	va_start( ap, s );
-	do {
-		s2 = va_arg( ap, newstr * );
-		if ( s2 ) newstr_empty( s2 );
-	} while ( s2 );
-	va_end( ap );
-}
-
-void
-newstr_addchar( newstr *s, char newchar )
-{
-	assert( s );
-	if ( newchar=='\0' ) return; /* appending '\0' is a null operation */
-	if ( !s->data || s->dim==0 ) 
-		newstr_initalloc( s, newstr_initlen );
-	if ( s->len + 2 > s->dim ) 
-		newstr_realloc( s, s->len*2 );
-	s->data[s->len++] = newchar;
-	s->data[s->len] = '\0';
-}
-
-/* newstr_addutf8
- *
- * Add potential multibyte character to s starting at pointer p.
- * Multibyte Unicode characters have the high bit set.
- *
- * Since we can progress more than one byte at p, return the
- * properly updated pointer p.
- */
-const char *
-newstr_addutf8( newstr *s, const char *p )
-{
-	if ( ! ((*p) & 128 ) ) {
-		newstr_addchar( s, *p );
-		p++;
-	} else {
-		while ( ((*p) & 128) ) {
-			newstr_addchar( s, *p );
-			p++;
-		}
-	}
-	return p;
-}
-
-void 
-newstr_fprintf( FILE *fp, newstr *s )
-{
-	assert( s );
-	if ( s->data ) fprintf( fp, "%s", s->data );
-}
-
-void
-newstr_prepend( newstr *s, const char *addstr )
-{
-	unsigned long lenaddstr, i;
-	assert( s && addstr );
-	lenaddstr = strlen( addstr );
-	if ( lenaddstr==0 ) return;
-	if ( !s->data || !s->dim )
-		newstr_initalloc( s, lenaddstr+1 );
-	else {
-		if ( s->len + lenaddstr  + 1 > s->dim )
-			newstr_realloc( s, s->len + lenaddstr + 1 );
-		for ( i=s->len+lenaddstr-1; i>=lenaddstr; i-- )
-			s->data[i] = s->data[i-lenaddstr];
-	}
-	strncpy( s->data, addstr, lenaddstr );
-	s->len += lenaddstr;
-	s->data[ s->len ] = '\0';
-}
-
-static inline void
-newstr_strcat_ensurespace( newstr *s, unsigned long n )
-{
-	unsigned long m = s->len + n + 1;
-	if ( !s->data || !s->dim )
-		newstr_initalloc( s, m );
-	else if ( s->len + n + 1 > s->dim )
-		newstr_realloc( s, m );
-}
-
-static inline void 
-newstr_strcat_internal( newstr *s, const char *addstr, unsigned long n )
-{
-	newstr_strcat_ensurespace( s, n );
-	strncat( &(s->data[s->len]), addstr, n );
-	s->len += n;
-	s->data[s->len]='\0';
-}
-
-void
-newstr_newstrcat( newstr *s, newstr *old )
-{
-	assert ( s && old );
-	if ( !old->data ) return;
-	else newstr_strcat_internal( s, old->data, old->len );
-}
-
-void
-newstr_strcat( newstr *s, const char *addstr )
-{
-	unsigned long n;
-	assert( s && addstr );
-	n = strlen( addstr );
-	newstr_strcat_internal( s, addstr, n );
-}
-
-void
-newstr_segcat( newstr *s, char *startat, char *endat )
-{
-	unsigned long n;
-	char *p;
-
-	assert( s && startat && endat );
-	assert( (size_t) startat < (size_t) endat );
-
-	if ( startat==endat ) return;
-
-	n = 0;
-	p = startat;
-	while ( p!=endat ) {
-		n++;
-		p++;
-	}
-
-	newstr_strcat_internal( s, startat, n );
-}
-
-void
-newstr_indxcat( newstr *s, char *p, unsigned long start, unsigned long stop )
-{
-	unsigned long i;
-	assert( s && p );
-	assert( start <= stop );
-	for ( i=start; i<stop; ++i )
-		newstr_addchar( s, p[i] );
-}
-
-/* newstr_cpytodelim()
- *     term      = string of characters to be used as terminators
- *     finalstep = set to non-zero to position return value past the
- *                 terminating character
- */
-char *
-newstr_cpytodelim( newstr *s, char *p, const char *delim, unsigned char finalstep )
-{
-	newstr_empty( s );
-	return newstr_cattodelim( s, p, delim, finalstep );
-}
-
-/* newstr_cpytodelim()
- *     term      = string of characters to be used as terminators
- *     finalstep = set to non-zero to position return value past the
- *                 terminating character
- */
-char *
-newstr_cattodelim( newstr *s, char *p, const char *delim, unsigned char finalstep )
-{
-	while ( p && *p && !strchr( delim, *p ) ) {
-		newstr_addchar( s, *p );
-		p++;
-	}
-	if ( *p && finalstep ) p++;
-	return p;
-}
-
-static inline void
-newstr_strcpy_ensurespace( newstr *s, unsigned long n )
-{
-	unsigned long m = n + 1;
-	if ( !s->data || !s->dim )
-		newstr_initalloc( s, m );
-	else if ( m > s->dim )
-		newstr_realloc( s, m );
-}
-
-static inline void
-newstr_strcpy_internal( newstr *s, const char *p, unsigned long n )
-{
-	newstr_strcpy_ensurespace( s, n );
-	strncpy( s->data, p, n );
-	s->data[n] = '\0';
-	s->len = n;
-}
-
-void
-newstr_newstrcpy( newstr *s, newstr *old )
-{
-	assert( s );
-	if ( s==old ) return;
-	else if ( !old || old->len==0 ) newstr_empty( s );
-	else newstr_strcpy_internal( s, old->data, old->len );
-}
-
-void 
-newstr_strcpy( newstr *s, const char *addstr )
-{
-	unsigned long n;
-	assert( s && addstr );
-	n = strlen( addstr );
-	newstr_strcpy_internal( s, addstr, n );
-}
-
-/* newstr_segcpy( s, start, end );
- *
- * copies [start,end) into s
- */
-void
-newstr_segcpy( newstr *s, char *startat, char *endat )
-{
-	unsigned long n;
-	char *p;
-
-	assert( s && startat && endat );
-	assert( ((size_t) startat) <= ((size_t) endat) );
-
-	if ( startat==endat ) {
-		newstr_empty( s );
-		return;
-	}
-
-	n = 0;
-	p = startat;
-	while ( p!=endat ) {
-		p++;
-		n++;
-	}
-
-	newstr_strcpy_internal( s, startat, n );
-}
-
-/*
- * newstr_indxcpy( s, in, start, stop );
- *
- * copies in[start,stop) (excludes stop) into s
- */
-void
-newstr_indxcpy( newstr *s, char *p, unsigned long start, unsigned long stop )
-{
-	unsigned long i;
-	assert( s && p );
-	assert( start <= stop );
-	if ( start == stop ) {
-		newstr_empty( s );
-		return;
-	}
-	newstr_strcpy_ensurespace( s, stop-start+1 );
-	for ( i=start; i<stop; ++i )
-		s->data[i-start] = p[i];
-	s->len = stop-start;
-	s->data[s->len] = '\0';
-}
-
-newstr *
-newstr_strdup( const char *s1 )
-{
-	newstr *s2 = newstr_new();
-	if ( s2 )
-		newstr_strcpy( s2, s1 );
-	return s2;
-}
-
-void
-newstr_segdel( newstr *s, char *p, char *q )
-{
-	newstr tmp1, tmp2;
-	char *r;
-	assert( s );
-	r = &(s->data[s->len]);
-	newstr_init( &tmp1 );
-	newstr_init( &tmp2 );
-	newstr_segcpy( &tmp1, s->data, p );
-	newstr_segcpy( &tmp2, q, r );
-	newstr_empty( s );
-	if ( tmp1.data ) newstr_strcat( s, tmp1.data );
-	if ( tmp2.data ) newstr_strcat( s, tmp2.data );
-	newstr_free( &tmp2 );
-	newstr_free( &tmp1 );
-}
-
-/*
- * newstr_findreplace()
- *
- *   if replace is "" or NULL, then delete find
- */
-
-int
-newstr_findreplace( newstr *s, const char *find, const char *replace )
-{
-	long diff;
-	size_t findstart, searchstart;
-	size_t p1, p2;
-	size_t find_len, rep_len, curr_len;
-	char empty[2] = "";
-	unsigned long minsize;
-	char *p;
-	int n = 0;
-
-	assert( s && find );
-	if ( !s->data || !s->dim ) return n;
-	if ( !replace ) replace = empty;
-
-	find_len = strlen( find );
-	rep_len  = strlen( replace );
-	diff     = rep_len - find_len;
-	if ( diff < 0 ) diff = 0;
-
-	searchstart=0;
-	while ((p=strstr(s->data + searchstart,find))!=NULL) {
-		curr_len = strlen(s->data);
-		findstart=(size_t) p - (size_t) s->data;
-		minsize = curr_len + diff + 1;
-	 	if (s->dim <= minsize) newstr_realloc( s, minsize );
-		if ( find_len > rep_len ) {
-			p1 = findstart + rep_len;
-			p2 = findstart + find_len;
-			while( s->data[p2] )
-				s->data[p1++]=s->data[p2++];
-			s->data[p1]='\0';
-			n++;
-		} else if ( find_len < rep_len ) {
-			for ( p1=curr_len; p1>=findstart+find_len; p1-- )
-				s->data[p1+diff] = s->data[p1];
-			n++;
-		}
-		for (p1=0; p1<rep_len; p1++)
-			s->data[findstart+p1]=replace[p1];
-		searchstart = findstart + rep_len; 
-		s->len += rep_len - find_len;
-	}
-	return n;
-}
-
-
-/* newstr_fget()
- *   returns 0 if we're done, 1 if we're not done
- *   extracts line by line (regardless of end characters)
- *   and feeds from buf....
- */
-int
-newstr_fget( FILE *fp, char *buf, int bufsize, int *pbufpos, newstr *outs )
-{
-	int  bufpos = *pbufpos, done = 0;
-	char *ok;
-	assert( fp && outs );
-	newstr_empty( outs );
-	while ( !done ) {
-		while ( buf[bufpos] && buf[bufpos]!='\r' && buf[bufpos]!='\n' )
-			newstr_addchar( outs, buf[bufpos++] );
-		if ( buf[bufpos]=='\0' ) {
-			ok = fgets( buf, bufsize, fp );
-			bufpos=*pbufpos=0;
-			if ( !ok && feof(fp) ) { /* end-of-file */
-				buf[bufpos] = 0;
-				if ( outs->len==0 ) return 0; /*nothing in out*/
-				else return 1; /*one last out */
-			}
-		} else if ( buf[bufpos]=='\r' || buf[bufpos]=='\n' ) done=1;
-	}
-	if ( ( buf[bufpos]=='\n' && buf[bufpos+1]=='\r') ||
-	     ( buf[bufpos]=='\r' && buf[bufpos+1]=='\n') ) bufpos+=2;
-	else if ( buf[bufpos]=='\n' || buf[bufpos]=='\r' ) bufpos+=1; 
-	*pbufpos = bufpos;
-	return 1;
-}
-
-void
-newstr_toupper( newstr *s )
-{
-	unsigned long i;
-	assert( s );
-	for ( i=0; i<s->len; ++i )
-		s->data[i] = toupper( (unsigned char)s->data[i] );
-}
-
-void
-newstr_tolower( newstr *s )
-{
-	unsigned long i;
-	assert( s );
-	for ( i=0; i<s->len; ++i )
-		s->data[i] = tolower( (unsigned char)s->data[i] );
-}
-
-/* newstr_swapstrings( s1, s2 )
- * be sneaky and swap internal newstring data from one
- * string to another
- */
-void
-newstr_swapstrings( newstr *s1, newstr *s2 )
-{
-	char *tmpp;
-	int tmp;
-
-	assert( s1 && s2 );
-
-	/* swap dimensioning info */
-	tmp = s1->dim;
-	s1->dim = s2->dim;
-	s2->dim = tmp;
-
-	/* swap length info */
-	tmp = s1->len;
-	s1->len = s2->len;
-	s2->len = tmp;
-
-	/* swap data */
-	tmpp = s1->data;
-	s1->data = s2->data;
-	s2->data = tmpp;
-}
-
-void
-newstr_trimstartingws( newstr *s )
-{
-	char *p, *q;
-	int n;
-
-	assert( s );
-
-	if ( s->len==0 || !is_ws( s->data[0] ) ) return;
-
-	n = 0;
-	p = s->data;
-	while ( is_ws( *p ) ) p++;
-
-	q = s->data;
-	while ( *p ) {
-		*q++ = *p++;
-		n++;
-	}
-	*q = '\0';
-
-	s->len = n;
-}
-
-void
-newstr_trimendingws( newstr *s )
-{
-	assert( s );
-	while ( s->len > 0 && is_ws( s->data[s->len-1] ) ) {
-		s->data[s->len-1] = '\0';
-		s->len--;
-	}
-}
-
-int
-newstr_match_first( newstr *s, char ch )
-{
-	assert( s );
-	if ( !s->len ) return 0;
-	if ( s->data[0] == ch ) return 1;
-	return 0;
-}
-
-int
-newstr_match_end( newstr *s, char ch )
-{
-	assert( s );
-	if ( !s->len ) return 0;
-	if ( s->data[ s->len - 1 ] == ch ) return 1;
-	return 0;
-}
-
-void
-newstr_trimbegin( newstr *s, unsigned long n )
-{
-	char *p, *q;
-
-	assert( s );
-
-	if ( n==0 ) return;
-	if ( s->len==0 ) return;
-	if ( n >= s->len ) {
-		newstr_empty( s );
-		return;
-	}
-
-	p = s->data;
-	while ( n-- > 0 ) p++;
-
-	n = 0;
-	q = s->data;
-	while ( *p ) {
-		*q++ = *p++;
-		n++;
-	}
-	*q = '\0';
-
-	s->len = n;
-}
-
-void
-newstr_trimend( newstr *s, unsigned long n )
-{
-	assert( s );
-
-	if ( n==0 ) return;
-	if ( n >= s->len ) {
-		newstr_empty( s );
-		return;
-	}
-
-	s->len -= n;
-	s->data[ s->len ] = '\0';
-}
-
-void
-newstr_pad( newstr *s, unsigned long len, char ch )
-{
-	unsigned long i;
-	assert( s );
-	for ( i=s->len; i<len; i++ )
-		newstr_addchar( s, ch );
-}
-
-void
-newstr_copyposlen( newstr *s, newstr *in, unsigned long pos, unsigned long len )
-{
-	unsigned long i, max;
-	assert( s );
-	newstr_empty( s );
-	max = pos+len;
-	if ( max > in->len ) max = in->len;
-	for ( i=pos; i<max; ++i )
-		newstr_addchar( s, in->data[i] );
-}
-
-static void
-newstr_check_case( newstr *s, int *lowercase, int *uppercase )
-{
-	int i;
-	assert( s );
-	*lowercase = 0;
-	*uppercase = 0;
-	if ( s->len < 1 ) return;
-	for ( i=0; i<s->len && !( *lowercase && *uppercase ); ++i ) {
-		if ( isalpha( (unsigned char)s->data[i] ) ) {
-			if ( isupper( (unsigned char)s->data[i] ) ) *uppercase += 1;
-			else if ( islower( (unsigned char)s->data[i] ) ) *lowercase += 1;
-		}
-	}
-}
-
-int
-newstr_is_mixedcase( newstr *s )
-{
-	int lowercase, uppercase;
-	newstr_check_case( s, &lowercase, &uppercase );
-	if ( lowercase > 0 && uppercase > 0 ) return 1;
-	return 0;
-}
-
-int
-newstr_is_lowercase( newstr *s )
-{
-	int lowercase, uppercase;
-	newstr_check_case( s, &lowercase, &uppercase );
-	if ( lowercase > 0 && uppercase == 0 ) return 1;
-	return 0;
-}
-
-int
-newstr_is_uppercase( newstr *s )
-{
-	int lowercase, uppercase;
-	newstr_check_case( s, &lowercase, &uppercase );
-	if ( lowercase == 0 && uppercase > 0 ) return 1;
-	return 0;
-}
-
-void
-newstr_stripws( newstr *s )
-{
-	unsigned long len = 0;
-	char *p, *q;
-	assert( s );
-	if ( s->len ) {
-		p = q = s->data;
-		while ( *p ) {
-			if ( !is_ws( *p ) ) {
-				*q = *p;
-				q++;
-				len++;
-			}
-			p++;
-		}
-		*q = '\0';
-	}
-	s->len = len;
-}
-
-int
-newstr_newstrcmp( const newstr *s, const newstr *t )
-{
-	assert( s );
-	assert( t );
-	if ( s->len == 0 && t->len == 0 ) return 0;
-	if ( s->len == 0 ) return strcmp( "", t->data );
-	if ( t->len == 0 ) return strcmp( s->data, "" );
-	return strcmp( s->data, t->data );
-}
-
-void
-newstr_reverse( newstr *s )
-{
-	unsigned long i, max;
-	char tmp;
-	assert( s );
-	max = s->len / 2;
-	for ( i=0; i<max; ++i ) {
-		tmp = s->data[ i ];
-		s->data[ i ] = s->data[ s->len - 1 - i ];
-		s->data[ s->len - 1 - i ] = tmp;
-	}
-}
-
-int
-newstr_fgetline( newstr *s, FILE *fp )
-{
-	int ch, eol = 0;
-	assert( s );
-	assert( fp );
-	newstr_empty( s );
-	if ( feof( fp ) ) return 0;
-	while ( !feof( fp ) && !eol ) {
-		ch = fgetc( fp );
-		if ( ch == EOF ) {
-			if ( s->len ) return 1;
-			else return 0;
-		}
-		else if ( ch == '\n' ) eol = 1;
-		else if ( ch == '\r' ) {
-			ch = fgetc( fp );
-			if ( ch != '\n' ) ungetc( ch, fp );
-			eol = 1;
-		} else {
-			newstr_addchar( s, (char) ch );
-		}
-	}
-	return 1;
-}
-
-/*
- * s = "Hi!\0", s.len = 3
- *
- * newstr_char( s, 0 ) = 'H'  newstr_revchar( s, 0 ) = '!'
- * newstr_char( s, 1 ) = 'i'  newstr_revchar( s, 1 ) = 'i'
- * newstr_char( s, 2 ) = '!'  newstr_revchar( s, 2 ) = 'H'
- * newstr_char( s, 3 ) = '\0' newstr_revchar( s, 3 ) = '\0'
- */
-char
-newstr_char( newstr *s, unsigned long n )
-{
-	assert( s );
-	if ( s->len==0 || n >= s->len ) return '\0';
-	return s->data[ n ];
-}
-
-char
-newstr_revchar( newstr *s, unsigned long n )
-{
-	assert( s );
-	if ( s->len==0 || n >= s->len ) return '\0';
-	return s->data[ s->len - n - 1];
-}
-
-void
-newstr_makepath( newstr *path, const char *dirname, const char *filename, char sep )
-{
-	assert( path );
-	if ( dirname ) newstr_strcpy( path, dirname );
-	else newstr_empty( path );
-
-	if ( path->len && path->data[path->len-1]!=sep )
-		newstr_addchar( path, sep );
-
-	if ( filename ) newstr_strcat( path, filename );
-}
diff --git a/bibutils/newstr.h b/bibutils/newstr.h
deleted file mode 100644
--- a/bibutils/newstr.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * newstr.h
- *
- * Version: 04/27/14
- *
- * Copyright (c) Chris Putnam 1999-2014
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef NEWSTR_H
-#define NEWSTR_H
-
-#include <stdio.h>
-
-typedef struct newstr {
-	char *data;
-	unsigned long dim;
-	unsigned long len;
-}  newstr;
-
-newstr *newstr_new         ( void ); 
-void    newstr_delete      ( newstr *s );
-
-void    newstr_init        ( newstr *s );
-void    newstr_initstr     ( newstr *s, const char *initstr );
-void    newstr_empty       ( newstr *s );
-void    newstr_free        ( newstr *s );
-
-void    newstrs_init       ( newstr *s, ... );
-void    newstrs_empty      ( newstr *s, ... );
-void    newstrs_free       ( newstr *s, ... );
-
-void newstr_mergestrs   ( newstr *s, ... );
-newstr *newstr_strdup   ( const char *p );
-void newstr_addchar     ( newstr *s, char newchar );
-void newstr_reverse     ( newstr *s );
-const char *newstr_addutf8    ( newstr *s, const char *p );
-void newstr_strcat      ( newstr *s, const char *addstr );
-void newstr_newstrcat   ( newstr *s, newstr *old );
-void newstr_segcat      ( newstr *s, char *startat, char *endat );
-char *newstr_cpytodelim  ( newstr *s, char *p, const char *delim, unsigned char finalstep );
-char *newstr_cattodelim  ( newstr *s, char *p, const char *delim, unsigned char finalstep );
-void newstr_prepend     ( newstr *s, const char *addstr );
-void newstr_strcpy      ( newstr *s, const char *addstr );
-void newstr_newstrcpy   ( newstr *s, newstr *old );
-void newstr_segcpy      ( newstr *s, char *startat, char *endat );
-void newstr_segdel      ( newstr *s, char *startat, char *endat );
-void newstr_indxcpy     ( newstr *s, char *p, unsigned long start, unsigned long stop );
-void newstr_indxcat     ( newstr *s, char *p, unsigned long start, unsigned long stop );
-void newstr_fprintf     ( FILE *fp, newstr *s );
-int  newstr_fget        ( FILE *fp, char *buf, int bufsize, int *pbufpos,
-                          newstr *outs );
-char newstr_char        ( newstr *s, unsigned long n );
-char newstr_revchar     ( newstr *s, unsigned long n );
-int  newstr_fgetline    ( newstr *s, FILE *fp );
-int  newstr_findreplace ( newstr *s, const char *find, const char *replace );
-void newstr_toupper     ( newstr *s );
-void newstr_tolower     ( newstr *s );
-void newstr_trimstartingws( newstr *s );
-void newstr_trimendingws( newstr *s );
-void newstr_swapstrings ( newstr *s1, newstr *s2 );
-void newstr_stripws     ( newstr *s );
-
-int  newstr_match_first ( newstr *s, char ch );
-int  newstr_match_end   ( newstr *s, char ch );
-void newstr_trimbegin   ( newstr *s, unsigned long n );
-void newstr_trimend     ( newstr *s, unsigned long n );
-
-void newstr_pad         ( newstr *s, unsigned long len, char ch );
-void newstr_copyposlen  ( newstr *s, newstr *in, unsigned long pos, unsigned long len );
-
-void newstr_makepath    ( newstr *path, const char *dirname, const char *filename, char sep );
-
-
-int  newstr_is_mixedcase( newstr *s );
-int  newstr_is_lowercase( newstr *s );
-int  newstr_is_uppercase( newstr *s );
-
-int  newstr_newstrcmp    ( const newstr *s, const newstr *t );
-
-int  newstr_memerr( newstr *s );
-
-
-/* #define NEWSTR_PARANOIA
- *
- * set to clear memory before it is freed or reallocated
- * note that this is slower...may be important if string
- * contains sensitive information
- */
-
-/* #define NEWSTR_NOASSERT
- *
- * set to turn off the use of asserts (and associated call to exit)
- * in newstr functions...useful for library construction for
- * Linux distributions that don't want libraries calling exit, but
- * not useful during code development
- */
-
-#endif
-
diff --git a/bibutils/newstr_conv.c b/bibutils/newstr_conv.c
deleted file mode 100644
--- a/bibutils/newstr_conv.c
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * newstr_conv.c
- *
- * Copyright (c) Chris Putnam 1999-2014
- *
- * Source code released under the GPL version 2
- *
- * newstring routines for converting newstrings between character sets
- *
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <limits.h>
-#include "newstr.h"
-#include "latex.h"
-#include "entities.h"
-#include "utf8.h"
-#include "gb18030.h"
-#include "charsets.h"
-#include "newstr_conv.h"
-
-static void
-addentity( newstr *s, unsigned int ch )
-{
-	char buf[512];
-	sprintf( buf, "&#%d;", ch );
-	newstr_strcat( s, buf );
-}
-
-/* These are the five minimal predefined entites in XML */
-static int
-minimalxmlchars( newstr *s, unsigned int ch )
-{
-	if ( ch==34 ) { newstr_strcat( s, "&quot;" ); return 1; }
-	else if ( ch==38 ) { newstr_strcat( s, "&amp;" ); return 1; }
-	else if ( ch==39 ) { newstr_strcat( s, "&apos;" ); return 1; }
-	else if ( ch==60 ) { newstr_strcat( s, "&lt;" ); return 1; }
-	else if ( ch==62 ) { newstr_strcat( s, "&gt;" ); return 1; }
-	return 0;
-}
-
-static void
-addxmlchar( newstr *s, unsigned int ch )
-{
-	if ( minimalxmlchars( s, ch ) ) return;
-	if ( ch > 127 ) addentity( s, ch );
-	else newstr_addchar( s, ch );
-}
-
-static void
-addutf8char( newstr *s, unsigned int ch, int xmlout )
-{
-	unsigned char code[6];
-	int nc, i;
-	if ( xmlout ) {
-		if ( minimalxmlchars( s, ch ) ) return;
-		if ( ch > 127 && xmlout == NEWSTR_CONV_XMLOUT_ENTITIES )
-			{ addentity( s, ch ); return; }
-	}
-	nc = utf8_encode( ch, code );
-	for ( i=0; i<nc; ++i )
-		newstr_addchar( s, code[i] );
-}
-
-static void
-addgb18030char( newstr *s, unsigned int ch, int xmlout )
-{
-	unsigned char code[4];
-	int nc, i;
-	if ( xmlout ) {
-		if ( minimalxmlchars( s, ch ) ) return;
-		if ( ch > 127 && xmlout == NEWSTR_CONV_XMLOUT_ENTITIES )
-			{ addentity( s, ch ); return; }
-	}
-	nc = gb18030_encode( ch, code );
-	for ( i=0; i<nc; ++i )
-		newstr_addchar( s, code[i] );
-}
-
-static void
-addlatexchar( newstr *s, unsigned int ch, int xmlout, int utf8out )
-{
-	char buf[512];
-	uni2latex( ch, buf, sizeof( buf ) );
-	/* If the unicode character isn't recognized as latex output
-	 * a '?' unless the user has requested unicode output.  If so,
-	 * output the unicode.
-	 */
-	if ( utf8out && !strcmp( buf, "?" ) ) {
-		addutf8char( s, ch, xmlout );
-	} else {
-		newstr_strcat( s, buf );
-	}
-}
-
-/*
- * get_unicode()
- * 
- *   This can be a little tricky.  If the character is simply encoded
- *   such as UTF8 for > 128 or by numeric xml entities such as "&#534;"
- *   then the output of decode_entity() and utf8_decode will necessarily
- *   be in the charsetin character set.  On the other hand, if it's a
- *   fancy latex expression, such as "\alpha", or a non-numeric xml entity
- *   like "&amp;", then we'll get the Unicode value (because our lists only
- *   keep the Unicode equivalent).
- *
- *   The unicode variable indicates whether or not a Unicode-based listing
- *   was used to convert the character (remember that charsetin could be
- *   Unicode independently).
- *
- *   The charset variable is used to keep track of what character set
- *   the character is in prior to conversion.
- *
- */
-
-static unsigned int
-get_unicode( newstr *s, unsigned int *pi, int charsetin, int latexin, int utf8in, int xmlin )
-{
-	unsigned int ch;
-	int unicode = 0, err = 0;
-	if ( xmlin && s->data[*pi]=='&' ) {
-		ch = decode_entity( s->data, pi, &unicode, &err );
-	} else if ( charsetin==CHARSET_GB18030 ) {
-		ch = gb18030_decode( s->data, pi );
-		unicode = 1;
-	} else if ( latexin ) {
-		/* Must handle bibtex files in UTF8/Unicode */
-		if ( utf8in && ( s->data[*pi] & 128 ) ) {
-			ch = utf8_decode( s->data, pi );
-			unicode = 1;
-		} else ch = latex2char( s->data, pi, &unicode );
-	}
-	else if ( utf8in )
-		ch = utf8_decode( s->data, pi );
-	else {
-		ch = (unsigned int) s->data[*pi];
-		*pi = *pi + 1;
-	}
-	if ( !unicode && charsetin!=CHARSET_UNICODE )
-		ch = charset_lookupchar( charsetin, ch );
-	return ch;
-}
-
-static int
-write_unicode( newstr *s, unsigned int ch, int charsetout, int latexout,
-		int utf8out, int xmlout )
-{
-	unsigned int c;
-	if ( latexout ) {
-		addlatexchar( s, ch, xmlout, utf8out );
-	} else if ( utf8out ) {
-		addutf8char( s, ch, xmlout );
-	} else if ( charsetout==CHARSET_GB18030 ) {
-		addgb18030char( s, ch, xmlout );
-	} else {
-		c = charset_lookupuni( charsetout, ch );
-		if ( xmlout ) addxmlchar( s, c );
-		else newstr_addchar( s, c );
-	}
-	return 1;
-}
-
-/*
- * Returns 1 on memory error condition
- */
-int
-newstr_convert( newstr *s,
-	int charsetin,  int latexin,  int utf8in,  int xmlin,
-	int charsetout, int latexout, int utf8out, int xmlout )
-{
-	unsigned int pos = 0;
-	unsigned int ch;
-	newstr ns;
-	int ok = 1;
-
-	if ( !s || s->len==0 ) return ok;
-
-	newstr_init( &ns );
-
-	if ( charsetin==CHARSET_UNKNOWN ) charsetin = CHARSET_DEFAULT;
-	if ( charsetout==CHARSET_UNKNOWN ) charsetout = CHARSET_DEFAULT;
-
-	while ( s->data[pos] ) {
-		ch = get_unicode( s, &pos, charsetin, latexin, utf8in, xmlin );
-		ok = write_unicode( &ns, ch, charsetout, latexout, utf8out, xmlout );
-		if ( !ok ) goto out;
-	}
-
-	newstr_swapstrings( s, &ns );
-out:
-	newstr_free( &ns );
-
-	return ok;
-}
-
diff --git a/bibutils/newstr_conv.h b/bibutils/newstr_conv.h
deleted file mode 100644
--- a/bibutils/newstr_conv.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * newstring_conv.h
- *
- * Copyright (c) Chris Putnam 1999-2014
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef NEWSTR_CONV_H
-#define NEWSTR_CONV_H
-
-#define NEWSTR_CONV_XMLOUT_FALSE    (0)
-#define NEWSTR_CONV_XMLOUT_TRUE     (1)
-#define NEWSTR_CONV_XMLOUT_ENTITIES (3)
-
-#include "newstr.h"
-
-extern int newstr_convert( newstr *s,
-		int charsetin, int latexin, int utf8in, int xmlin, 
-		int charsetout, int latexout, int utf8out, int xmlout );
-
-#endif
-
diff --git a/bibutils/notes.c b/bibutils/notes.c
new file mode 100644
--- /dev/null
+++ b/bibutils/notes.c
@@ -0,0 +1,102 @@
+/*
+ * notes.c
+ *
+ * Copyright (c) Chris Putnam 2016-2017
+ *
+ * Program and source code released under the GPL version 2
+ *
+ */
+#include <string.h>
+#include "url.h"
+#include "notes.h"
+
+/*
+ * notes are mostly directly copies; however, lots of formats hide
+ * URLs/DOIs in the notes fields. For example:
+ *
+ * For RIS, Oxford Journals hides DOI in the N1 field.
+ * For Endnote, Wiley hides DOI in the %1 field.
+ * etc.
+ */
+
+typedef struct url_t {
+	char *prefix;
+	char *tag;
+	int offset;
+} url_t;
+
+static void
+notes_added_url( fields *bibout, str *invalue, int level, int *ok )
+{
+	url_t prefixes[] = {
+		{ "arXiv:",                                    "ARXIV",     6 },
+		{ "http://arxiv.org/abs/",                     "ARXIV",    21 },
+		{ "jstor:",                                    "JSTOR",     6 },
+		{ "http://www.jstor.org/stable/",              "JSTOR",    28 },
+		{ "medline:",                                  "MEDLINE",   8 },
+		{ "pubmed:",                                   "PMID",      7 },
+		{ "http://www.ncbi.nlm.nih.gov/pubmed/",       "PMID",     35 },
+		{ "http://www.ncbi.nlm.nih.gov/pmc/articles/", "PMC",      41 },
+		{ "http://dx.doi.org/",                        "DOI",      19 },
+		{ "isi:",                                      "ISIREFNUM", 4 },
+	};
+	int nprefixes = sizeof( prefixes ) / sizeof( prefixes[0] );
+
+	char *p = invalue->data;
+	char *tag = "URL";
+	int fstatus;
+	int i;
+
+	/* bibtex/biblatex-specific */
+	if ( !strncasecmp( p, "\\urllink", 8 ) ) p += 8;
+	if ( !strncasecmp( p, "\\url", 4 ) ) p += 4;
+
+	for ( i=0; i<nprefixes; ++i ) {
+		if ( !strncasecmp( p, prefixes[i].prefix, prefixes[i].offset ) ) {
+			tag = prefixes[i].tag;
+			p   = p + prefixes[i].offset;
+			break;
+		}
+	}
+
+	fstatus = fields_add( bibout, tag, p, level );
+
+	if ( fstatus==FIELDS_OK ) *ok = 1;
+	else *ok = 0;
+}
+
+static int
+notes_added_doi( fields *bibout, str *invalue, int level, int *ok )
+{
+	int doi, fstatus;
+
+	doi = is_doi( invalue->data );
+
+	if ( doi != -1 ) {
+		fstatus = fields_add( bibout, "DOI", &(invalue->data[doi]), level );
+		if ( fstatus != FIELDS_OK ) *ok = 0;
+		return 1;
+	}
+
+	else return 0;
+}
+
+int
+notes_add( fields *bibout, str *invalue, int level )
+{
+	int fstatus, done = 0, ok = 1;
+
+	if ( !is_embedded_link( invalue->data ) ) {
+		fstatus = fields_add( bibout, "NOTES", invalue->data, level );
+		if ( fstatus != FIELDS_OK ) ok = 0;
+	}
+
+	else {
+
+		done = notes_added_doi( bibout, invalue, level, &ok );
+		if ( !done ) notes_added_url( bibout, invalue, level, &ok );
+
+	}
+
+	return ok;
+}
diff --git a/bibutils/notes.h b/bibutils/notes.h
new file mode 100644
--- /dev/null
+++ b/bibutils/notes.h
@@ -0,0 +1,17 @@
+/*
+ * notes.h
+ *
+ * Copyright (c) Chris Putnam 2016-2017
+ *
+ * Source code released under the GPL version 2
+ *
+ */
+#ifndef NOTES_H
+#define NOTES_H
+
+#include "bibutils.h"
+
+int notes_add( fields *bibout, str *invalue, int level );
+
+#endif
+
diff --git a/bibutils/pages.c b/bibutils/pages.c
new file mode 100644
--- /dev/null
+++ b/bibutils/pages.c
@@ -0,0 +1,87 @@
+/*
+ * pages.c
+ *
+ * Copyright (c) Chris Putnam 2016-2017
+ *
+ * Program and source code released under GPL verison 2
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "is_ws.h"
+#include "utf8.h"
+#include "pages.h"
+
+/* extract_range()
+ *
+ * Handle input strings like:
+ *
+ * "1-15"
+ * " 1 - 15 "
+ * " 1000--- 1500"
+ * " 1 <<em-dash>> 10"
+ * " 107 111"
+ */
+static void
+extract_range( str *input, str *begin, str *end )
+{
+	/* -30 is the first character of a UTF8 em-dash and en-dash */
+	const char terminators[] = { ' ', '-', '\t', '\r', '\n', -30, '\0' };
+	char *p;
+
+	str_empty( begin );
+	str_empty( end );
+
+	if ( input->len==0 ) return;
+
+	p = skip_ws( input->data );
+	while ( *p && !strchr( terminators, *p ) )
+		str_addchar( begin, *p++ );
+
+	p = skip_ws( p );
+
+	while ( *p=='-' ) p++;
+	while ( utf8_is_emdash( p ) ) p+=3;
+	while ( utf8_is_endash( p ) ) p+=3;
+
+	p = skip_ws( p );
+
+	while ( *p && !strchr( terminators, *p ) )
+		str_addchar( end, *p++ );
+}
+
+int
+pages_add( fields *bibout, char *outtag, str *invalue, int level )
+{
+	int fstatus, status = 1;
+	str start, stop;
+
+	str_init( &start );
+	str_init( &stop );
+
+	extract_range( invalue, &start, &stop );
+
+	if ( str_memerr( &start ) || str_memerr( &stop ) ) {
+		status = 0;
+		goto out;
+	}
+
+	if ( start.len>0 ) {
+		fstatus = fields_add( bibout, "PAGES:START", start.data, level );
+		if ( fstatus!=FIELDS_OK ) {
+			status = 0;
+			goto out;
+		}
+	}
+
+	if ( stop.len>0 ) {
+		fstatus = fields_add( bibout, "PAGES:STOP", stop.data, level );
+		if ( fstatus!=FIELDS_OK ) status = 0;
+	}
+
+out:
+	str_free( &start );
+	str_free( &stop );
+	return status;
+}
+
diff --git a/bibutils/pages.h b/bibutils/pages.h
new file mode 100644
--- /dev/null
+++ b/bibutils/pages.h
@@ -0,0 +1,16 @@
+/*
+ * pages.h
+ *
+ * Copyright (c) Chris Putnam 2016-2017
+ *
+ * Program and source code released under the GPL version 2
+ */
+#ifndef PAGES_H
+#define PAGES_H
+
+#include "bibl.h"
+
+int pages_add( fields *bibout, char *outtag, str *invalue, int level );
+
+#endif
+
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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Source code released under the GPL version 2
  *
@@ -13,16 +13,27 @@
 #include "reftypes.h"
 
 int
-get_reftype( char *p, long refnum, char *progname, variants *all, int nall, char *tag )
+get_reftype( char *p, long refnum, char *progname, variants *all, int nall, char *tag, int *is_default, int chattiness )
 {
 	int i;
+
 	p = skip_ws( p );
-	for ( i=0; i<nall; ++i )
+
+	*is_default = 0;
+
+	for ( i=0; i<nall; ++i ) {
 		if ( !strncasecmp( all[i].type, p, strlen(all[i].type) ) ) 
 			return i;
-	if ( progname ) fprintf( stderr, "%s: ", progname );
-	fprintf( stderr, "Did not recognize type '%s' of refnum %ld (%s).\n"
-		"\tDefaulting to article.\n", p, refnum, tag );
+	}
+
+	*is_default = 1;
+
+	if ( chattiness==REFTYPE_CHATTY ) {
+		if ( progname ) fprintf( stderr, "%s: ", progname );
+		fprintf( stderr, "Did not recognize type '%s' of refnum %ld (%s).\n"
+			"\tDefaulting to %s.\n", p, refnum, tag, all[0].type );
+	}
+
 	return 0;
 }
 
@@ -52,6 +63,7 @@
 		*processingtype = ((all[reftype]).tags[n]).processingtype;
 		*level          = ((all[reftype]).tags[n]).level;
 		*newtag         = ((all[reftype]).tags[n]).newstr;
+		return 1;
 	}
-	return n;
+	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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Source code released under the GPL version 2
  *
@@ -9,9 +9,13 @@
 #ifndef REFTYPES_H
 #define REFTYPES_H
 
+#define REFTYPE_CHATTY  (0)
+#define REFTYPE_SILENT  (1)
+
 /* Reftypes handled by core code */
 #define ALWAYS          (0)
 #define DEFAULT         (1)
+#define SKIP            (2)
 
 /* Reftypes to be handled by converters */
 #define SIMPLE          (2)
@@ -26,17 +30,18 @@
 #define HOWPUBLISHED    (11)
 #define LINKEDFILE      (12)
 #define KEYWORD         (13)
-#define BT_URL          (14) /* Bibtex URL */
-#define BT_NOTE         (15) /* Bibtex Note */
-#define BT_SENTE        (16) /* Bibtex 'Sente' */
-#define BT_GENRE        (17) /* Bibtex Genre */
-#define BT_EPRINT       (18) /* Bibtex 'Eprint' */
-#define BT_ORG          (19) /* Bibtex Organization */
-#define BLT_THESIS_TYPE (20) /* Biblatex Thesis Type */
-#define BLT_SCHOOL      (21) /* Biblatex School */
-#define BLT_EDITOR      (22) /* Biblatex Editor */
-#define BLT_SUBTYPE     (23) /* Biblatex entrysubtype */
-#define BLT_SKIP        (24) /* Biblatex Skip Entry */
+#define URL             (14)
+#define BT_SENTE        (15) /* Bibtex 'Sente' */
+#define BT_GENRE        (16) /* Bibtex Genre */
+#define BT_EPRINT       (17) /* Bibtex 'Eprint' */
+#define BT_ORG          (18) /* Bibtex Organization */
+#define BLT_THESIS_TYPE (19) /* Biblatex Thesis Type */
+#define BLT_SCHOOL      (20) /* Biblatex School */
+#define BLT_EDITOR      (21) /* Biblatex Editor */
+#define BLT_SUBTYPE     (22) /* Biblatex entrysubtype */
+#define BLT_SKIP        (23) /* Biblatex Skip Entry */
+#define EPRINT          (24)
+#define NUM_REFTYPES    (25)
 
 typedef struct {
 	char *oldstr;
@@ -51,10 +56,8 @@
 	int     ntags;
 } variants;
 
-extern int get_reftype( char *q, long refnum, char *progname, variants *all, int nall, char *tag );
-extern int process_findoldtag( char *oldtag, int reftype, variants all[], int nall );
-extern int translate_oldtag( char *oldtag, int reftype, variants all[], int nall, int *processingtype, int *level, char **newtag );
-
-
+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 );
 
 #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-2014
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Source code released under the GPL version 2
  *
@@ -10,19 +10,30 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
-#include "newstr.h"
-#include "newstr_conv.h"
+#include "str.h"
+#include "str_conv.h"
 #include "fields.h"
 #include "name.h"
 #include "title.h"
+#include "url.h"
+#include "utf8.h"
 #include "serialno.h"
 #include "reftypes.h"
-#include "doi.h"
-#include "risin.h"
+#include "bibformats.h"
+#include "generic.h"
 
+extern variants ris_all[];
+extern int ris_nall;
+
 /*****************************************************
  PUBLIC: void risin_initparams()
 *****************************************************/
+
+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_convertf( fields *risin, fields *info, int reftype, param *p );
+
 void
 risin_initparams( param *p, const char *progname )
 {
@@ -45,8 +56,8 @@
 	p->all      = ris_all;
 	p->nall     = ris_nall;
 
-	list_init( &(p->asis) );
-	list_init( &(p->corps) );
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
 
 	if ( !progname ) p->progname = NULL;
 	else p->progname = strdup( progname );
@@ -63,77 +74,114 @@
     character 4 = space (ansi 32)
     character 5 = dash (ansi 45)
     character 6 = space (ansi 32)
+
+  some sources don't have a space at character 6 if there
+  is no data (such as "ER  -" records). Handle this.
+
+  www.omicsonline.org mangles the RIS specification and
+  puts _three_ spaces before the dash.  Handle this too.
 */
 static int
-risin_istag( char *buf )
+is_ris_tag( 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 (buf[4]!='-') return 0;
-	if (buf[5]!=' ') return 0;
-	return 1;
+
+	/*...RIS fits specifications 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;
+	}
+
+	return 0;
 }
 
 static int
-readmore( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line )
+is_ris_start_tag( char *p )
 {
+	/* ...TY tag that fits specifications */
+	if ( !strncmp( p, "TY  - ",  6 ) ) return 1;
+	/* ...TY tag with an extra space? */
+	if ( !strncmp( p, "TY   - ", 7 ) ) return 1;
+	return 0;
+}
+
+static int
+is_ris_end_tag( char *p )
+{
+	/* ...ER tag that fits specifications */
+	if ( !strncmp( p, "ER  -",  5 ) ) return 1;
+	/* ...ER tag with an extra space? */
+	if ( !strncmp( p, "ER   -", 6 ) ) return 1;
+	return 0;
+}
+
+static int
+readmore( FILE *fp, char *buf, int bufsize, int *bufpos, str *line )
+{
 	if ( line->len ) return 1;
-	else return newstr_fget( fp, buf, bufsize, bufpos, line );
+	else return str_fget( fp, buf, bufsize, bufpos, line );
 }
 
-int
-risin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, 
-		newstr *reference, int *fcharset )
+static int
+risin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
 	int haveref = 0, inref = 0, readtoofar = 0;
-	unsigned char *up;
 	char *p;
+
 	*fcharset = CHARSET_UNKNOWN;
+
 	while ( !haveref && readmore( fp, buf, bufsize, bufpos, line ) ) {
-		if ( !line->data || line->len==0 ) continue;
+
+		if ( str_is_empty( line ) ) continue;
+
 		p = &( line->data[0] );
-		/* Recognize UTF8 BOM */
-		up = (unsigned char * ) p;
-		if ( line->len > 2 && 
-				up[0]==0xEF && up[1]==0xBB && up[2]==0xBF ) {
+
+		if ( utf8_is_bom( p ) ) {
 			*fcharset = CHARSET_UNICODE;
 			p += 3;
 		}
-		/* Each reference starts with 'TY  - ' && 
-		 * ends with 'ER  - ' */
-		if ( strncmp(p,"TY  - ",6)==0 ) {
-			if ( !inref ) {
-				inref = 1;
-			} else {
+
+		/* References are bounded by tags 'TY  - ' && 'ER  - ' */
+		if ( is_ris_start_tag( p ) ) {
+			if ( !inref ) inref = 1;
+			else {
 				/* we've read too far.... */
 				readtoofar = 1;
 				inref = 0;
 			}
 		}
-		if ( risin_istag( p ) ) {
+
+		if ( is_ris_tag( p ) ) {
 			if ( !inref ) {
 				fprintf(stderr,"Warning.  Tagged line not "
 					"in properly started reference.\n");
 				fprintf(stderr,"Ignored: '%s'\n", p );
-			} else if ( !strncmp(p,"ER  -",5) ) {
+			} else if ( is_ris_end_tag( p ) ) {
 				inref = 0;
 			} else {
-				newstr_addchar( reference, '\n' );
-				newstr_strcat( reference, p );
+				str_addchar( reference, '\n' );
+				str_strcatc( reference, p );
 			}
 		}
 		/* not a tag, but we'll append to last values ...*/
-		else if ( inref && strncmp(p,"ER  -",5)) {
-			newstr_addchar( reference, '\n' );
-			newstr_strcat( reference, p );
+		else if ( inref && !is_ris_end_tag( p ) ) {
+			str_addchar( reference, '\n' );
+			str_strcatc( reference, p );
 		}
 		if ( !inref && reference->len ) haveref = 1;
-		if ( !readtoofar ) newstr_empty( line );
+		if ( !readtoofar ) str_empty( line );
 	}
+
 	if ( inref ) haveref = 1;
+
 	return haveref;
 }
 
@@ -142,103 +190,143 @@
 *****************************************************/
 
 static char*
-process_line2( newstr *tag, newstr *data, char *p )
+process_untagged_line( str *value, char *p )
 {
 	while ( *p==' ' || *p=='\t' ) p++;
 	while ( *p && *p!='\r' && *p!='\n' )
-		newstr_addchar( data, *p++ );
+		str_addchar( value, *p++ );
 	while ( *p=='\r' || *p=='\n' ) p++;
 	return p;
 }
 
 static char*
-process_line( newstr *tag, newstr *data, char *p )
+process_tagged_line( str *tag, str *value, char *p )
 {
 	int i = 0;
-	while ( i<6 && *p ) {
-		if ( i<2 ) newstr_addchar( tag, *p );
+
+	while ( i<6 && *p && *p!='\n' && *p!='\r' ) {
+		if ( i<2 ) str_addchar( tag, *p );
 		p++;
 		i++;
 	}
+
 	while ( *p==' ' || *p=='\t' ) p++;
+
 	while ( *p && *p!='\r' && *p!='\n' )
-		newstr_addchar( data, *p++ );
-	newstr_trimendingws( data );
+		str_addchar( value, *p++ );
+	str_trimendingws( value );
+
 	while ( *p=='\n' || *p=='\r' ) p++;
+
 	return p;
 }
 
-int
-risin_processf( fields *risin, char *p, char *filename, long nref )
+static int
+merge_tag_value( fields *risin, str *tag, str *value, int *tag_added )
 {
-	newstr tag, data;
-	int status, n;
+	str *oldval;
+	int n, status;
 
-	newstrs_init( &tag, &data, NULL );
+	if ( str_has_value( value ) ) {
+		if ( *tag_added==1 ) {
+			n = fields_num( risin );
+			if ( n>0 ) {
+				oldval = fields_value( risin, n-1, FIELDS_STRP );
+				str_addchar( oldval, ' ' );
+				str_strcat( oldval, value );
+				if ( str_memerr( oldval ) ) return BIBL_ERR_MEMERR;
+			}
+		}
+		else  {
+			status = fields_add( risin, str_cstr( tag ), str_cstr( value ), 0 );
+			if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+			*tag_added = 1;
+		}
+	}
+	return BIBL_OK;
+}
 
+static int
+add_tag_value( fields *risin, str *tag, str *value, int *tag_added )
+{
+	int status;
+
+	if ( str_has_value( value ) ) {
+		status = fields_add( risin, str_cstr( tag ), str_cstr( value ), 0 );
+		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+		*tag_added = 1;
+	}
+
+	else {
+		*tag_added = 0;
+	}
+
+	return BIBL_OK;
+}
+
+static int
+risin_processf( fields *risin, char *p, char *filename, long nref, param *pm )
+{
+	int status, tag_added = 0, ret = 1;
+	str tag, value;
+
+	strs_init( &tag, &value, NULL );
+
 	while ( *p ) {
-		if ( risin_istag( p ) ) {
-		p = process_line( &tag, &data, p );
-		/* no anonymous fields allowed */
-		if ( tag.len )
-			status = fields_add( risin, tag.data, data.data, 0 );
-			if ( status!=FIELDS_OK ) return 0;
-		} else {
-			p = process_line2( &tag, &data, p );
-			n = fields_num( risin );
-			if ( data.len && n>0 ) {
-				newstr *od;
-				od = fields_value( risin, n-1, FIELDS_STRP );
-				newstr_addchar( od, ' ' );
-				newstr_strcat( od, data.data );
+
+		/* ...tag, add entry */
+		if ( is_ris_tag( p ) ) {
+			str_empty( &tag );
+			str_empty( &value );
+			p = process_tagged_line( &tag, &value, p );
+			status = add_tag_value( risin, &tag, &value, &tag_added );
+			if ( status!=BIBL_OK ) {
+				ret = 0;
+				goto out;
 			}
 		}
-		newstrs_empty( &tag, &data, NULL );
+
+		/* ...no tag, merge with previous line */
+		else {
+			str_empty( &value );
+			p = process_untagged_line( &value, p );
+			status = merge_tag_value( risin, &tag, &value, &tag_added );
+			if ( status!=BIBL_OK ) {
+				ret = 0;
+				goto out;
+			}
+		}
+
 	}
+out:
 
-	newstrs_free( &tag, &data, NULL );
-	return 1;
+	strs_free( &tag, &value, NULL );
+	return ret;
 }
 
 /*****************************************************
  PUBLIC: int risin_typef()
 *****************************************************/
 
-int
-risin_typef( fields *risin, char *filename, int nref, param *p, variants *all, int nall )
+static int
+risin_typef( fields *risin, char *filename, int nref, param *p )
 {
-	char *refnum = "";
-	int n, reftype, nreftype;
-	n = fields_find( risin, "TY", 0 );
-	nreftype = fields_find( risin, "ID", 0 );
-	if ( nreftype!=-1 ) refnum = risin[n].data->data;
-	if ( n!=-1 )
-		reftype = get_reftype( (risin[n].data)->data, nref, p->progname,
-			all, nall, refnum );
-	else
-		reftype = get_reftype( "", nref, p->progname, all, nall, refnum ); /*default */
-	return reftype;
+	int ntypename, nrefname, is_default;
+	char *refname = "", *typename = "";
+
+	ntypename = fields_find( risin, "TY", LEVEL_MAIN );
+	nrefname  = fields_find( risin, "ID", LEVEL_MAIN );
+	if ( ntypename!=-1 ) typename = fields_value( risin, ntypename, FIELDS_CHRP_NOUSE );
+	if ( nrefname!=-1 )  refname  = fields_value( risin, nrefname,  FIELDS_CHRP_NOUSE );
+
+	return get_reftype( typename, nref, p->progname, p->all, p->nall, refname, &is_default, REFTYPE_CHATTY );
 }
 
 /*****************************************************
  PUBLIC: int risin_convertf()
 *****************************************************/
 
-/* oxfordjournals hide the DOI in the NOTES N1 field */
 static int
-risin_addnotes( fields *f, char *tag, newstr *s, int level )
-{
-	int doi, status;
-	doi = is_doi( s->data );
-	if ( doi!=-1 )
-		status = fields_add( f, "DOI", &(s->data[doi]), level );
-	else
-		status = fields_add( f, tag, s->data, level );
-	if ( status==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
-
-static int
 is_uri_file_scheme( char *p )
 {
 	if ( !strncmp( p, "file:", 5 ) ) return 5;
@@ -246,209 +334,214 @@
 }
 
 static int
-is_uri_remote_scheme( char *p )
-{
-	char *scheme[] = { "http:", "ftp:", "git:", "gopher:" };
-	int i, len, nschemes = sizeof( scheme ) / sizeof( scheme[0] );
-	for ( i=0; i<nschemes; ++i ) {
-		len = strlen( scheme[i] );
-		if ( !strncmp( p, scheme[i], len ) ) return len;
-	}
-	return 0;
-}
-
-static int
-risin_addfile( fields *f, char *tag, newstr *s, int level )
+risin_linkedfile( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
 {
-	int status, n;
+	int fstatus, m;
 	char *p;
 
 	/* if URL is file:///path/to/xyz.pdf, only store "///path/to/xyz.pdf" */
-	n = is_uri_file_scheme( s->data );
-	if ( n ) {
+	m = is_uri_file_scheme( str_cstr( invalue ) );
+	if ( m ) {
 		/* skip past "file:" and store only actual path */
-		p = s->data + n;
-		status = fields_add( f, tag, p, level );
-		if ( status==FIELDS_OK ) return BIBL_OK;
+		p = invalue->data + m;
+		fstatus = fields_add( bibout, outtag, p, level );
+		if ( fstatus==FIELDS_OK ) return BIBL_OK;
 		else return BIBL_ERR_MEMERR;
 	}
 
 	/* if URL is http:, ftp:, etc. store as a URL */
-	n = is_uri_remote_scheme( s->data );
-	if ( n ) {
-		status = fields_add( f, "URL", s->data, level );
-		if ( status==FIELDS_OK ) return BIBL_OK;
+	m = is_uri_remote_scheme( str_cstr( invalue ) );
+	if ( m!=-1 ) {
+		fstatus = fields_add( bibout, "URL", str_cstr( invalue ), level );
+		if ( fstatus==FIELDS_OK ) return BIBL_OK;
 		else return BIBL_ERR_MEMERR;
 	}
 
 	/* badly formed, RIS wants URI, but store value anyway */
-	status = fields_add( f, tag, s->data, level );
-	if ( status==FIELDS_OK ) return BIBL_OK;
+	fstatus = fields_add( bibout, outtag, str_cstr( invalue ), level );
+	if ( fstatus==FIELDS_OK ) return BIBL_OK;
 	else return BIBL_ERR_MEMERR;
 }
 
 /* scopus puts DOI in the DO or DI tag, but it needs cleaning */
 static int
-risin_adddoi( fields *f, char *tag, newstr *s, int level )
+risin_doi( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
 {
-	int status, doi;
-	doi = is_doi( s->data );
+	int fstatus, doi;
+	doi = is_doi( str_cstr( invalue ) );
 	if ( doi!=-1 ) {
-		status = fields_add( f, "DOI", &(s->data[doi]), level );
-		if ( status==FIELDS_OK ) return BIBL_OK;
-		else return BIBL_ERR_MEMERR;
+		fstatus = fields_add( bibout, "DOI", &(invalue->data[doi]), level );
+		if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
-	else return BIBL_OK;
+	return BIBL_OK;
 }
 
 static int
-risin_adddate( fields *f, char *tag, newstr *d, int level )
+risin_date( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
 {
-	char *p = d->data;
-	newstr date;
+	char *p = invalue->data;
 	int part, status;
+	str date;
 
-	part = ( !strncasecmp( tag, "PART", 4 ) );
+	part = ( !strncasecmp( outtag, "PART", 4 ) );
 
-	newstr_init( &date );
-	while ( *p && *p!='/' ) newstr_addchar( &date, *p++ );
+	str_init( &date );
+	while ( *p && *p!='/' ) str_addchar( &date, *p++ );
+	if ( str_memerr( &date ) ) return BIBL_ERR_MEMERR;
 	if ( *p=='/' ) p++;
-	if ( date.len>0 ) {
-		if ( part ) status = fields_add( f, "PARTYEAR", date.data, level );
-		else        status = fields_add( f, "YEAR",     date.data, level );
+	if ( str_has_value( &date ) ) {
+		if ( part ) status = fields_add( bibout, "PARTDATE:YEAR", str_cstr( &date ), level );
+		else        status = fields_add( bibout, "DATE:YEAR",     str_cstr( &date ), level );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 
-	newstr_empty( &date );
-	while ( *p && *p!='/' ) newstr_addchar( &date, *p++ );
+	str_empty( &date );
+	while ( *p && *p!='/' ) str_addchar( &date, *p++ );
+	if ( str_memerr( &date ) ) return BIBL_ERR_MEMERR;
 	if ( *p=='/' ) p++;
-	if ( date.len>0 ) {
-		if ( part ) status = fields_add( f, "PARTMONTH", date.data, level );
-		else        status = fields_add( f, "MONTH",     date.data, level );
+	if ( str_has_value( &date ) ) {
+		if ( part ) status = fields_add( bibout, "PARTDATE:MONTH", str_cstr( &date ), level );
+		else        status = fields_add( bibout, "DATE:MONTH",     str_cstr( &date ), level );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 
-	newstr_empty( &date );
-	while ( *p && *p!='/' ) newstr_addchar( &date, *p++ );
+	str_empty( &date );
+	while ( *p && *p!='/' ) str_addchar( &date, *p++ );
+	if ( str_memerr( &date ) ) return BIBL_ERR_MEMERR;
 	if ( *p=='/' ) p++;
-	if ( date.len>0 ) {
-		if ( part ) status = fields_add( f, "PARTDAY", date.data, level );
-		else        status = fields_add( f, "DAY",     date.data, level );
+	if ( str_has_value( &date ) ) {
+		if ( part ) status = fields_add( bibout, "PARTDATE:DAY", str_cstr( &date ), level );
+		else        status = fields_add( bibout, "DATE:DAY",     str_cstr( &date ), level );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
 
-	newstr_empty( &date );
-	while ( *p ) newstr_addchar( &date, *p++ );
-	if ( date.len>0 ) {
-		if ( part ) status = fields_add( f, "PARTDATEOTHER", date.data,level);
-		else        status = fields_add( f, "DATEOTHER", date.data, level );
+	str_empty( &date );
+	while ( *p ) str_addchar( &date, *p++ );
+	if ( str_memerr( &date ) ) return BIBL_ERR_MEMERR;
+	if ( str_has_value( &date ) ) {
+		if ( part ) status = fields_add( bibout, "PARTDATE:OTHER", str_cstr( &date ), level );
+		else        status = fields_add( bibout, "DATE:OTHER",     str_cstr( &date ), level );
 		if ( status!=FIELDS_OK ) return BIBL_ERR_MEMERR;
 	}
-	newstr_free( &date );
+	str_free( &date );
 	return BIBL_OK;
 }
 
-static void
-risin_report_notag( param *p, char *tag )
-{
-	if ( p->verbose && strcmp( tag, "TY" ) ) {
-		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-		fprintf( stderr, "Did not identify RIS tag '%s'\n", tag );
-	}
-}
-
 static int
-risin_simple( fields *f, char *tag, char *value, int level )
+risin_person( fields *bibin, int n, str *intag, str *invalue, int level, param *pm, char *outtag, fields *bibout )
 {
-	int fstatus = fields_add( f, tag, value, level );
-	if ( fstatus==FIELDS_OK ) return BIBL_OK;
-	else return BIBL_ERR_MEMERR;
-}
+	int i, begin, end, ok, status = BIBL_OK;
+	slist tokens;
+	str name;
 
-int
-risin_convertf( fields *risin, fields *f, int reftype, param *p, variants *all, int nall )
-{
-	int process, level, i, n, nfields, ok, fstatus, status = BIBL_OK;
-	char *outtag, *tag, *value;
-	newstr *t, *d;
+	str_init( &name );
+	slist_init( &tokens );
 
-	nfields = fields_num( risin );
+	status = slist_tokenize( &tokens, invalue, " \t\r\n", 1 );
+	if ( status!=SLIST_OK ) { status = BIBL_ERR_MEMERR; goto out; }
 
-	for ( i=0; i<nfields; ++i ) {
-		t = fields_tag( risin, i, FIELDS_STRP );
-		n = translate_oldtag( t->data, reftype, all, nall, &process, &level, &outtag );
-		if ( n==-1 ) {
-			risin_report_notag( p, t->data );
-			continue;
+	begin = 0;
+	while ( begin < tokens.n ) {
+
+		end = begin + 1;
+
+		while ( end < tokens.n && strcasecmp( slist_cstr( &tokens, end ), "and" ) )
+			end++;
+
+		str_empty( &name );
+		for ( i=begin; i<end; ++i ) {
+			if ( i>begin ) str_addchar( &name, ' ' );
+			str_strcat( &name, slist_str( &tokens, i ) );
 		}
-		if ( process==ALWAYS ) continue; /* add in core code */
 
-		d = fields_value( risin, i, FIELDS_STRP );
+		ok = name_add( bibout, outtag, str_cstr( &name ), level, &(pm->asis), &(pm->corps) );
+		if ( !ok ) { status = BIBL_ERR_MEMERR; goto out; }
 
-		switch ( process ) {
+		begin = end + 1;
 
-		case SIMPLE:
-			status = risin_simple( f, outtag, d->data, level );
-			break;
+		/* Handle repeated 'and' errors */
+		while ( begin < tokens.n && !strcasecmp( slist_cstr( &tokens, begin ), "and" ) )
+			begin++;
 
-		case PERSON:
-			ok = name_add( f, outtag, d->data, level, &(p->asis), &(p->corps) );
-			if ( ok ) status = BIBL_OK;
-			else status = BIBL_ERR_MEMERR;
-			break;
+	}
 
-		case TITLE:
-			ok = title_process( f, outtag, d->data, level, p->nosplittitle );
-			if ( ok ) status = BIBL_OK;
-			else status = BIBL_ERR_MEMERR;
-			break;
+out:
+	str_free( &name );
+	slist_free( &tokens );
+	return status;
+}
 
-		case SERIALNO:
-			ok = addsn( f, d->data, level );
-			if ( ok ) status = BIBL_OK;
-			else status = BIBL_ERR_MEMERR;
-			break;
+/* look for thesis-type hint */
+static int
+risin_thesis_hints( fields *bibin, int reftype, param *p, fields *bibout )
+{
+	int i, nfields, fstatus;
+	char *tag, *value;
 
-		case DATE:
-			status = risin_adddate( f, outtag, d, level );
-			break;
+	if ( strcasecmp( p->all[reftype].type, "THES" ) ) return BIBL_OK;
 
-		case NOTES:
-			status = risin_addnotes( f, outtag, d, level );
-			break;
+	nfields = fields_num( bibin );
+	for ( i=0; i<nfields; ++i ) {
+		tag = fields_tag( bibin, i, FIELDS_CHRP );
+		if ( strcasecmp( tag, "U1" ) ) continue;
+		value = fields_value( bibin, i, FIELDS_CHRP );
+		if ( !strcasecmp(value,"Ph.D. Thesis")||
+		     !strcasecmp(value,"Masters Thesis")||
+		     !strcasecmp(value,"Diploma Thesis")||
+		     !strcasecmp(value,"Doctoral Thesis")||
+		     !strcasecmp(value,"Habilitation Thesis")) {
+			fstatus = fields_add( bibout, "GENRE", value, 0 );
+			if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
+		}
+	}
+	return BIBL_OK;
+}
 
-		case DOI:
-			status = risin_adddoi( f, outtag, d, level );
-			break;
+static void
+risin_report_notag( param *p, char *tag )
+{
+	if ( p->verbose && strcmp( tag, "TY" ) ) {
+		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
+		fprintf( stderr, "Did not identify RIS tag '%s'\n", tag );
+	}
+}
 
-		case LINKEDFILE:
-			status = risin_addfile( f, outtag, d, level );
-			break;
+static int
+risin_convertf( fields *bibin, fields *bibout, int reftype, param *p )
+{
+	static int (*convertfns[NUM_REFTYPES])(fields *, int, str *, str *, int, param *, char *, fields *) = {
+		[ 0 ... NUM_REFTYPES-1 ] = generic_null,
+		[ SIMPLE       ] = generic_simple,
+		[ TITLE        ] = generic_title,
+		[ PERSON       ] = risin_person,
+		[ SERIALNO     ] = generic_serialno,
+		[ NOTES        ] = generic_notes,
+		[ URL          ] = generic_url,
+		[ DATE         ] = risin_date,
+		[ DOI          ] = risin_doi,
+		[ LINKEDFILE   ] = risin_linkedfile,
+        };
+	int process, level, i, nfields, status = BIBL_OK;
+	str *intag, *invalue;
+	char *outtag;
 
-		default:
-			status = BIBL_OK;
-			break;
+	nfields = fields_num( bibin );
 
+	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 );
+			continue;
 		}
+		invalue = fields_value( bibin, i, FIELDS_STRP );
+
+		status = convertfns[ process ] ( bibin, i, intag, invalue, level, p, outtag, bibout );
 		if ( status!=BIBL_OK ) return status;
 	}
 
-	/* look for thesis-type hint */
-	if ( !strcasecmp( all[reftype].type, "THES" ) ) {
-		for ( i=0; i<nfields; ++i ) {
-			tag = fields_tag( risin, i, FIELDS_CHRP );
-			if ( strcasecmp( tag, "U1" ) ) continue;
-			value = fields_value( risin, i, FIELDS_CHRP );
-			if ( !strcasecmp(value,"Ph.D. Thesis")||
-			     !strcasecmp(value,"Masters Thesis")||
-			     !strcasecmp(value,"Diploma Thesis")||
-			     !strcasecmp(value,"Doctoral Thesis")||
-			     !strcasecmp(value,"Habilitation Thesis")) {
-				fstatus = fields_add( f, "GENRE", value, 0 );
-				if ( fstatus!=FIELDS_OK ) return BIBL_ERR_MEMERR;
-			}
-		}
-	}
+	if ( status == BIBL_OK ) status = risin_thesis_hints( bibin, reftype, p, bibout );
+
+	if ( status==BIBL_OK && p->verbose ) fields_report( bibout, stderr );
 
 	return status;
 }
diff --git a/bibutils/risin.h b/bibutils/risin.h
deleted file mode 100644
--- a/bibutils/risin.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * risin.h
- *
- * Copyright (c) Chris Putnam 2003-2014
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef RISIN_H
-#define RISIN_H
-
-#include "newstr.h"
-#include "fields.h"
-#include "reftypes.h"
-#include "bibutils.h"
-
-extern int risin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset );
-extern int risin_processf( fields *risin, char *p, char *filename, long nref );
-extern int risin_typef( fields *risin, char *filename, int nref, param *p, variants *all, int nall );
-extern int risin_convertf( fields *risin, fields *info, int reftype, param *p, variants *all, int nall ); 
-
-extern void risin_initparams( param *p, const char *progname );
-
-extern variants ris_all[];
-extern int ris_nall;
-
-#endif
-
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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Source code released under the GPL version 2
  *
@@ -11,13 +11,18 @@
 #include <string.h>
 #include <ctype.h>
 #include "utf8.h"
-#include "newstr.h"
+#include "str.h"
 #include "strsearch.h"
 #include "fields.h"
-#include "doi.h"
 #include "name.h"
-#include "risout.h"
+#include "title.h"
+#include "url.h"
+#include "bibformats.h"
 
+static int  risout_write( fields *info, FILE *fp, param *p, unsigned long refnum );
+static void risout_writeheader( FILE *outptr, param *p );
+
+
 void
 risout_initparams( param *p, const char *progname )
 {
@@ -44,7 +49,7 @@
 }
 
 enum { 
-	TYPE_UNKNOWN,
+	TYPE_UNKNOWN = 0,
 	TYPE_STD,                /* standard/generic */
 	TYPE_ABSTRACT,           /* abstract */
 	TYPE_ARTICLE,            /* article */
@@ -55,9 +60,9 @@
 	TYPE_ELEC,               /* electronic */
 	TYPE_HEAR,               /* hearing */
 	TYPE_MAGARTICLE,         /* magazine article */
-	TYPE_NEWS,               /* newspaper */
+	TYPE_NEWSPAPER,          /* newspaper */
 	TYPE_MPCT,               /* mpct */
-	TYPE_PAMP,               /* pamphlet */
+	TYPE_PAMPHLET,           /* pamphlet */
 	TYPE_PATENT,             /* patent */
 	TYPE_PCOMM,              /* personal communication */
 	TYPE_PROGRAM,            /* program */
@@ -71,43 +76,80 @@
 	TYPE_HABILITATIONTHESIS, /* thesis */
 	TYPE_MAP,                /* map, cartographic data */
 	TYPE_UNPUBLISHED,        /* unpublished */
+	NUM_TYPES
 };
 
+static int type_is_element[ NUM_TYPES ] = {
+	[ 0 ... NUM_TYPES-1 ] = 0,
+	[ TYPE_ARTICLE      ] = 1,
+	[ TYPE_INBOOK       ] = 1,
+	[ TYPE_MAGARTICLE   ] = 1,
+	[ TYPE_NEWSPAPER    ] = 1,
+	[ TYPE_ABSTRACT     ] = 1,
+	[ TYPE_CONF         ] = 1,
+};
+
+static int type_uses_journal[ NUM_TYPES ] = {
+	[ 0 ... NUM_TYPES-1 ] = 0,
+	[ TYPE_ARTICLE      ] = 1,
+	[ TYPE_MAGARTICLE   ] = 1,
+};
+
 static void
 write_type( FILE *fp, int type )
 {
-	switch( type ) {
-	case TYPE_UNKNOWN:            fprintf( fp, "TYPE_UNKNOWN" );            break;
-	case TYPE_STD:                fprintf( fp, "TYPE_STD" );                break;
-	case TYPE_ABSTRACT:           fprintf( fp, "TYPE_ABSTRACT" );           break;
-	case TYPE_ARTICLE:            fprintf( fp, "TYPE_ARTICLE" );            break;
-	case TYPE_BOOK:               fprintf( fp, "TYPE_BOOK" );               break;
-	case TYPE_CASE:               fprintf( fp, "TYPE_CASE" );               break;
-	case TYPE_INBOOK:             fprintf( fp, "TYPE_INBOOK" );             break;
-	case TYPE_CONF:               fprintf( fp, "TYPE_CONF" );               break;
-	case TYPE_ELEC:               fprintf( fp, "TYPE_ELEC" );               break;
-	case TYPE_HEAR:               fprintf( fp, "TYPE_HEAR" );               break;
-	case TYPE_MAGARTICLE:         fprintf( fp, "TYPE_MAGARTICLE" );         break;
-	case TYPE_NEWS:               fprintf( fp, "TYPE_NEWS" );               break;
-	case TYPE_MPCT:               fprintf( fp, "TYPE_MCPT" );               break;
-	case TYPE_PAMP:               fprintf( fp, "TYPE_PAMP" );               break;
-	case TYPE_PATENT:             fprintf( fp, "TYPE_PATENT" );             break;
-	case TYPE_PCOMM:              fprintf( fp, "TYPE_PCOMM" );              break;
-	case TYPE_PROGRAM:            fprintf( fp, "TYPE_PROGRAM" );            break;
-	case TYPE_REPORT:             fprintf( fp, "TYPE_REPORT" );             break;
-	case TYPE_STATUTE:            fprintf( fp, "TYPE_STATUTE" );            break;
-	case TYPE_THESIS:             fprintf( fp, "TYPE_THESIS" );             break;
-	case TYPE_MASTERSTHESIS:      fprintf( fp, "TYPE_MASTERSTHESIS" );      break;
-	case TYPE_PHDTHESIS:          fprintf( fp, "TYPE_PHDTHESIS" );          break;
-	case TYPE_DIPLOMATHESIS:      fprintf( fp, "TYPE_DIPLOMATHESIS" );      break;
-	case TYPE_DOCTORALTHESIS:     fprintf( fp, "TYPE_DOCTORALTHESIS" );     break;
-	case TYPE_HABILITATIONTHESIS: fprintf( fp, "TYPE_HABILITATIONTHESIS" ); break;
-	case TYPE_MAP:                fprintf( fp, "TYPE_MAP" );                break;
-	case TYPE_UNPUBLISHED:        fprintf( fp, "TYPE_UNPUBLISHED" );        break;
-	default:                      fprintf( fp, "Error - type not in enum" );break;
-	}
+	const char *typenames[ NUM_TYPES ] = {
+		[ TYPE_UNKNOWN            ] = "TYPE_UNKNOWN",
+		[ TYPE_STD                ] = "TYPE_STD",
+		[ TYPE_ABSTRACT           ] = "TYPE_ABSTRACT",
+		[ TYPE_ARTICLE            ] = "TYPE_ARTICLE",
+		[ TYPE_BOOK               ] = "TYPE_BOOK",
+		[ TYPE_CASE               ] = "TYPE_CASE",
+		[ TYPE_INBOOK             ] = "TYPE_INBOOK",
+		[ TYPE_CONF               ] = "TYPE_CONF",
+		[ TYPE_ELEC               ] = "TYPE_ELEC",
+		[ TYPE_HEAR               ] = "TYPE_HEAR",
+		[ TYPE_MAGARTICLE         ] = "TYPE_MAGARTICLE",
+		[ TYPE_NEWSPAPER          ] = "TYPE_NEWSPAPER",
+		[ TYPE_MPCT               ] = "TYPE_MPCT",
+		[ TYPE_PAMPHLET           ] = "TYPE_PAMPHLET",
+		[ TYPE_PATENT             ] = "TYPE_PATENT",
+		[ TYPE_PCOMM              ] = "TYPE_PCOMM",
+		[ TYPE_PROGRAM            ] = "TYPE_PROGRAM",
+		[ TYPE_REPORT             ] = "TYPE_REPORT",
+		[ TYPE_STATUTE            ] = "TYPE_STATUTE",
+		[ TYPE_THESIS             ] = "TYPE_THESIS",
+		[ TYPE_MASTERSTHESIS      ] = "TYPE_MASTERSTHESIS",
+		[ TYPE_PHDTHESIS          ] = "TYPE_PHDTHESIS",
+		[ TYPE_DIPLOMATHESIS      ] = "TYPE_DIPLOMATHESIS",
+		[ TYPE_DOCTORALTHESIS     ] = "TYPE_DOCTORALTHESIS",
+		[ TYPE_HABILITATIONTHESIS ] = "TYPE_HABILITATIONTHESIS",
+		[ TYPE_MAP                ] = "TYPE_MAP",
+		[ TYPE_UNPUBLISHED        ] = "TYPE_UNPUBLISHED",
+	};
+
+	if ( type < 0 || type >= NUM_TYPES ) fprintf( fp, "Error - type not in enum" );
+	else fprintf( fp, "%s", typenames[ type ] );
 }
 
+static void
+verbose_type_identified( char *element_type, param *p, int type )
+{
+	if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
+	fprintf( stderr, "Type from %s element: ", element_type );
+	write_type( stderr, type );
+	fprintf( stderr, "\n" );
+}
+
+static void
+verbose_type_assignment( char *tag, char *value, param *p, int type )
+{
+	if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
+	fprintf( stderr, "Type from tag '%s' data '%s': ", tag, value );
+	write_type( stderr, type );
+	fprintf( stderr, "\n" );
+}
+
 typedef struct match_type {
 	char *name;
 	int type;
@@ -125,7 +167,7 @@
 		{ "journal article",           TYPE_ARTICLE },
 		{ "magazine",                  TYPE_MAGARTICLE },
 		{ "conference publication",    TYPE_CONF },
-		{ "newspaper",                 TYPE_NEWS },
+		{ "newspaper",                 TYPE_NEWSPAPER },
 		{ "legislation",               TYPE_STATUTE },
 		{ "communication",             TYPE_PCOMM },
 		{ "hearing",                   TYPE_HEAR },
@@ -144,26 +186,19 @@
 		{ "map",                       TYPE_MAP },
 	};
 	int nmatch_genres = sizeof( match_genres ) / sizeof( match_genres[0] );
-	int type, i, j;
 	char *tag, *value;
+	int type, i, j;
 
 	type = TYPE_UNKNOWN;
 
 	for ( i=0; i<fields_num( f ); ++i ) {
-		if ( !fields_match_tag( f, i, "GENRE" ) &&
-		     !fields_match_tag( f, i, "NGENRE" ) )
-			continue;
+		tag = ( char * ) fields_tag( f, i, FIELDS_CHRP );
+		if ( strcmp( tag, "GENRE" ) && strcmp( tag, "NGENRE" ) ) continue;
 		value = ( char * ) fields_value( f, i, FIELDS_CHRP );
 		for ( j=0; j<nmatch_genres; ++j )
 			if ( !strcasecmp( match_genres[j].name, value ) )
 				type = match_genres[j].type;
-		if ( p->verbose ) {
-			tag = ( char * ) fields_tag( f, i, FIELDS_CHRP );
-			if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-			fprintf( stderr, "Type from tag '%s' data '%s': ", tag, value );
-			write_type( stderr, type );
-			fprintf( stderr, "\n" );
-		}
+		if ( p->verbose ) verbose_type_assignment( tag, value, p, type );
 		if ( type==TYPE_UNKNOWN ) {
 			if ( !strcasecmp( value, "periodical" ) )
 				type = TYPE_ARTICLE;
@@ -181,12 +216,7 @@
 
 	}
 
-	if ( p->verbose ) {
-		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-		fprintf( stderr, "Type from genre element: " );
-		write_type( stderr, type );
-		fprintf( stderr, "\n" );
-	}
+	if ( p->verbose ) verbose_type_identified( "genre", p, type );
 
 	return type;
 }
@@ -202,7 +232,8 @@
 		{ "cartographic",              TYPE_MAP     },
 	};
 	int nmatch_res = sizeof( match_res ) / sizeof( match_res[0] );
-	int type, i, j;
+	vplist_index i;
+	int type, j;
 	char *value;
 	vplist a;
 
@@ -217,20 +248,10 @@
 			if ( !strcasecmp( value, match_res[j].name ) )
 				type = match_res[j].type;
 		}
-		if ( p->verbose ) {
-			if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-			fprintf( stderr, "Type from tag 'RESOURCE' data '%s': ", value );
-			write_type( stderr, type );
-			fprintf( stderr, "\n" );
-		}
+		if ( p->verbose ) verbose_type_assignment( "RESOURCE", value, p, type );
 	}
 
-	if ( p->verbose ) {
-		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-		fprintf( stderr, "Type from resource element: " );
-		write_type( stderr, type );
-		fprintf( stderr, "\n" );
-	}
+	if ( p->verbose ) verbose_type_identified( "resource", p, type );
 
 	vplist_free( &a );
 	return type;
@@ -245,13 +266,13 @@
 	int i, monographic = 0, monographic_level = 0;
 //	int text = 0;
 	for ( i=0; i<f->n; ++i ) {
-		if ( !strcasecmp( f->tag[i].data, "issuance" ) &&
-		     !strcasecmp( f->data[i].data, "MONOGRAPHIC" ) ){
+		if ( !strcasecmp( (char *) fields_tag( f, i, FIELDS_CHRP_NOUSE ), "issuance" ) &&
+		     !strcasecmp( (char *) fields_value( f, i, FIELDS_CHRP_NOUSE ), "MONOGRAPHIC" ) ){
 			monographic = 1;
 			monographic_level = f->level[i];
 		}
-//		if ( !strcasecmp( f->tag[i].data, "typeOfResource" ) &&
-//		     !strcasecmp( f->data[i].data,"text") ) {
+//		if ( !strcasecmp( (char *) fields_tag( f, i, FIELDS_CHRP_NOUSE ), "typeOfResource" ) &&
+//		     !strcasecmp( (char *) fields_value( f, i, FIELDS_CHRP_NOUSE ), "text" ) ) {
 //			text = 1;
 //		}
 	}
@@ -295,242 +316,241 @@
 }
 
 static void
-output_type( FILE *fp, int type, param *p )
+append_type( int type, param *p, fields *out, int *status )
 {
-	match_type tyout[] = {
-		{ "STD",  TYPE_STD },
-		{ "ABST", TYPE_ABSTRACT },
-		{ "JOUR", TYPE_ARTICLE },
-		{ "BOOK", TYPE_BOOK },
-		{ "CASE", TYPE_CASE },
-		{ "CHAP", TYPE_INBOOK },
-		{ "CONF", TYPE_CONF },
-		{ "ELEC", TYPE_ELEC },
-		{ "HEAR", TYPE_HEAR },
-		{ "MGZN", TYPE_MAGARTICLE },
-		{ "NEWS", TYPE_NEWS },
-		{ "MPCT", TYPE_MPCT },
-		{ "PAMP", TYPE_PAMP },
-		{ "PAT",  TYPE_PATENT },
-		{ "PCOMM",TYPE_PCOMM },
-		{ "COMP", TYPE_PROGRAM },
-		{ "RPRT", TYPE_REPORT },
-		{ "STAT", TYPE_STATUTE },
-		{ "THES", TYPE_THESIS },
-		{ "THES", TYPE_MASTERSTHESIS },
-		{ "THES", TYPE_PHDTHESIS },
-		{ "THES", TYPE_DIPLOMATHESIS },
-		{ "THES", TYPE_DOCTORALTHESIS },
-		{ "THES", TYPE_HABILITATIONTHESIS },
-		{ "MAP",  TYPE_MAP },
-		{ "UNPB", TYPE_UNPUBLISHED }
+	char *typenames[ NUM_TYPES ] = {
+		[ TYPE_STD                ] = "STD",
+		[ TYPE_ABSTRACT           ] = "ABST",
+		[ TYPE_ARTICLE            ] = "JOUR",
+		[ TYPE_BOOK               ] = "BOOK",
+		[ TYPE_CASE               ] = "CASE",
+		[ TYPE_INBOOK             ] = "CHAP",
+		[ TYPE_CONF               ] = "CONF",
+		[ TYPE_ELEC               ] = "ELEC",
+		[ TYPE_HEAR               ] = "HEAR",
+		[ TYPE_MAGARTICLE         ] = "MGZN",
+		[ TYPE_NEWSPAPER          ] = "NEWS",
+		[ TYPE_MPCT               ] = "MPCT",
+		[ TYPE_PAMPHLET           ] = "PAMP",
+		[ TYPE_PATENT             ] = "PAT",
+		[ TYPE_PCOMM              ] = "PCOMM",
+		[ TYPE_PROGRAM            ] = "COMP",
+		[ TYPE_REPORT             ] = "RPRT",
+		[ TYPE_STATUTE            ] = "STAT",
+		[ TYPE_THESIS             ] = "THES",
+		[ TYPE_MASTERSTHESIS      ] = "THES",
+		[ TYPE_PHDTHESIS          ] = "THES",
+		[ TYPE_DIPLOMATHESIS      ] = "THES",
+		[ TYPE_DOCTORALTHESIS     ] = "THES",
+		[ TYPE_HABILITATIONTHESIS ] = "THES",
+		[ TYPE_MAP                ] = "MAP",
+		[ TYPE_UNPUBLISHED        ] = "UNPB",
 	};
-	int ntyout = sizeof( tyout ) / sizeof( tyout[0] );
-	int i, found;
+	int fstatus;
 
-	fprintf( fp, "TY  - " );
-	found = 0;
-	for ( i=0; i<ntyout && !found ; ++i ) {
-		if ( tyout[i].type == type ) {
-			fprintf( fp, "%s", tyout[i].name );
-			found = 1;
-		}
-	}
-	/* Report internal error, default to TYPE_STD */
-	if ( !found ) {
+	if ( type < 0 || type >= NUM_TYPES ) {
 		if ( p->progname ) fprintf( stderr, "%s: ", p->progname );
-		fprintf( stderr, "Internal Error: Cannot identify type %d\n",
-			type );
-		fprintf( fp, "STD" );
+		fprintf( stderr, "Internal error: Cannot recognize type %d, switching to TYPE_STD %d\n", type, TYPE_STD );
+		type = TYPE_STD;
 	}
-	fprintf( fp, "\n" );
+
+	fstatus = fields_add( out, "TY", typenames[ type ], LEVEL_MAIN );
+	if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 }
 
 static void
-output_people( FILE *fp, fields *f, char *tag, char *ristag, int level )
+append_people( fields *f, char *tag, char *ristag, int level, fields *out, int *status )
 {
-	newstr oneperson;
+	vplist_index i;
+	str oneperson;
 	vplist people;
-	int i;
-	newstr_init( &oneperson );
+	int fstatus;
+
+	str_init( &oneperson );
 	vplist_init( &people );
 	fields_findv_each( f, level, FIELDS_CHRP, &people, tag );
 	for ( i=0; i<people.n; ++i ) {
 		name_build_withcomma( &oneperson, ( char * ) vplist_get( &people, i ) );
-		fprintf( fp, "%s  - %s\n", ristag, oneperson.data );
+		if ( str_memerr( &oneperson ) ) { *status = BIBL_ERR_MEMERR; goto out; }
+		fstatus = fields_add_can_dup( out, ristag, str_cstr( &oneperson ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) { *status = BIBL_ERR_MEMERR; goto out; }
 	}
+out:
 	vplist_free( &people );
-	newstr_free( &oneperson );
+	str_free( &oneperson );
 }
 
 static void
-output_date( FILE *fp, fields *f )
+append_date( fields *in, fields *out, int *status )
 {
-	char *year  = fields_findv_firstof( f, LEVEL_ANY, FIELDS_CHRP,
-			"YEAR", "PARTYEAR", NULL );
-	char *month = fields_findv_firstof( f, LEVEL_ANY, FIELDS_CHRP,
-			"MONTH", "PARTMONTH", NULL );
-	char *day   = fields_findv_firstof( f, LEVEL_ANY, FIELDS_CHRP,
-			"DAY", "PARTDAY", NULL );
-	if ( year )
-		fprintf( fp, "PY  - %s\n", year );
+	char *year, *month, *day;
+	str date;
+	int fstatus;
+
+	year  = fields_findv_firstof( in, LEVEL_ANY, FIELDS_CHRP, "DATE:YEAR",  "PARTDATE:YEAR",  NULL );
+	month = fields_findv_firstof( in, LEVEL_ANY, FIELDS_CHRP, "DATE:MONTH", "PARTDATE:MONTH", NULL );
+	day   = fields_findv_firstof( in, LEVEL_ANY, FIELDS_CHRP, "DATE:DAY",   "PARTDATE:DAY",   NULL );
+
+	if ( year ) {
+		fstatus = fields_add( out, "PY", year, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+
 	if ( year || month || day ) {
-		fprintf( fp, "DA  - " );
-		if ( year ) fprintf( fp, "%s", year );
-		fprintf( fp, "/" );
-		if ( month ) fprintf( fp, "%s", month );
-		fprintf( fp, "/" );
-		if ( day ) fprintf( fp, "%s", day );
-		fprintf( fp, "\n" );
+		str_init( &date );
+
+		if ( year ) str_strcatc( &date, year );
+		str_addchar( &date, '/' );
+		if ( month ) str_strcatc( &date, month );
+		str_addchar( &date, '/' );
+		if ( day ) str_strcatc( &date, day );
+
+		if ( str_memerr( &date ) ) { *status = BIBL_ERR_MEMERR; str_free( &date ); return; }
+
+		fstatus = fields_add( out, "DA", str_cstr( &date ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+
+		str_free( &date );
 	}
 }
 
 static void
-output_titlecore( FILE *fp, fields *f, char *ristag, int level,
-	char *maintag, char *subtag )
+append_titlecore( fields *in, char *ristag, int level, char *maintag, char *subtag, fields *out, int *status )
 {
-	newstr *mainttl = fields_findv( f, level, FIELDS_STRP, maintag );
-	newstr *subttl  = fields_findv( f, level, FIELDS_STRP, subtag );
+	str *mainttl = fields_findv( in, level, FIELDS_STRP, maintag );
+	str *subttl  = fields_findv( in, level, FIELDS_STRP, subtag );
+	str fullttl;
+	int fstatus;
 
-	if ( !mainttl ) return;
+	str_init( &fullttl );
 
-	fprintf( fp, "%s  - %s", ristag, mainttl->data );
-	if ( subttl ) {
-		if ( mainttl->len > 0 &&
-		     mainttl->data[ mainttl->len - 1 ]!='?' )
-			fprintf( fp, ":" );
-		fprintf( fp, " %s", subttl->data );
+	title_combine( &fullttl, mainttl, subttl );
+
+	if ( str_memerr( &fullttl ) ) {
+		*status = BIBL_ERR_MEMERR;
+		goto out;
 	}
-	fprintf( fp, "\n" );
-}
 
-static int
-type_is_element( int type )
-{
-	if ( type==TYPE_ARTICLE )    return 1;
-	if ( type==TYPE_INBOOK )     return 1;
-	if ( type==TYPE_MAGARTICLE ) return 1;
-	if ( type==TYPE_NEWS )       return 1;
-	if ( type==TYPE_ABSTRACT )   return 1;
-	if ( type==TYPE_CONF )       return 1;
-	return 0;
-}
+	if ( str_has_value( &fullttl ) ) {
+		fstatus = fields_add( out, ristag, str_cstr( &fullttl ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 
-static int
-type_uses_journal( int type )
-{
-	if ( type==TYPE_ARTICLE )    return 1;
-	if ( type==TYPE_MAGARTICLE ) return 1;
-	return 0;
+out:
+	str_free( &fullttl );
 }
 
 static void
-output_alltitles( FILE *fp, fields *f, int type )
+append_alltitles( fields *in, int type, fields *out, int *status )
 {
-	output_titlecore( fp, f, "TI", 0, "TITLE", "SUBTITLE" );
-	output_titlecore( fp, f, "T2", -1, "SHORTTITLE", "SHORTSUBTITLE" );
-	if ( type_is_element( type ) ) {
-		if ( type_uses_journal( type ) )
-			output_titlecore( fp, f, "JO", 1, "TITLE", "SUBTITLE" );
-		else output_titlecore( fp, f, "BT", 1, "TITLE", "SUBTITLE" );
-		output_titlecore( fp, f, "T3", 2, "TITLE", "SUBTITLE" );
+	append_titlecore( in, "TI", 0, "TITLE", "SUBTITLE", out, status );
+	append_titlecore( in, "T2", -1, "SHORTTITLE", "SHORTSUBTITLE", out, status );
+	if ( type_is_element[ type ] ) {
+		if ( type_uses_journal[ type ] )
+			append_titlecore( in, "JO", 1, "TITLE", "SUBTITLE", out, status );
+		else append_titlecore( in, "BT", 1, "TITLE", "SUBTITLE", out, status );
+		append_titlecore( in, "T3", 2, "TITLE", "SUBTITLE", out, status );
 	} else {
-		output_titlecore( fp, f, "T3", 1, "TITLE", "SUBTITLE" );
+		append_titlecore( in, "T3", 1, "TITLE", "SUBTITLE", out, status );
 	}
 }
 
 static void
-output_pages( FILE *fp, fields *f )
+append_pages( fields *in, fields *out, int *status )
 {
-	char *sn = fields_findv( f, LEVEL_ANY, FIELDS_CHRP, "PAGESTART" );
-	char *en = fields_findv( f, LEVEL_ANY, FIELDS_CHRP, "PAGEEND" );
-	char *ar;
+	char *sn, *en, *ar;
+	int fstatus;
 
+	sn = fields_findv( in, LEVEL_ANY, FIELDS_CHRP, "PAGES:START" );
+	en = fields_findv( in, LEVEL_ANY, FIELDS_CHRP, "PAGES:STOP" );
+
 	if ( sn || en ) {
-		if ( sn ) fprintf( fp, "SP  - %s\n", sn );
-		if ( en ) fprintf( fp, "EP  - %s\n", en );
+		if ( sn ) {
+			fstatus = fields_add( out, "SP", sn, LEVEL_MAIN );
+			if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+		}
+		if ( en ) {
+			fstatus = fields_add( out, "EP", en, LEVEL_MAIN );
+			if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+		}
 	} else {
-		ar = fields_findv( f, LEVEL_ANY, FIELDS_CHRP, "ARTICLENUMBER" );
-		if ( ar ) fprintf( fp, "SP  - %s\n", ar );
+		ar = fields_findv( in, LEVEL_ANY, FIELDS_CHRP, "ARTICLENUMBER" );
+		if ( ar ) {
+			fstatus = fields_add( out, "SP", ar, LEVEL_MAIN );
+			if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+		}
 	}
 }
 
 static void
-output_keywords( FILE *fp, fields *f )
+append_keywords( fields *in, fields *out, int *status )
 {
+	vplist_index i;
+	int fstatus;
 	vplist vpl;
-	int i;
+
 	vplist_init( &vpl );
-	fields_findv_each( f, LEVEL_ANY, FIELDS_CHRP, &vpl, "KEYWORD" );
-	for ( i=0; i<vpl.n; ++i )
-		fprintf( fp, "KW  - %s\n", ( char * ) vplist_get( &vpl, i ) );
+	fields_findv_each( in, LEVEL_ANY, FIELDS_CHRP, &vpl, "KEYWORD" );
+	for ( i=0; i<vpl.n; ++i ) {
+		fstatus = fields_add( out, "KW", ( char * ) vplist_get( &vpl, i ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 	vplist_free( &vpl );
 }
 
 static void
-output_pmid( FILE *fp, fields *f )
+append_urls( fields *in, fields *out, int *status )
 {
-	newstr s;
-	int i;
-	newstr_init( &s );
-	for ( i=0; i<fields_num( f ); ++i ) {
-		if ( !fields_match_tag( f, i, "PMID" ) ) continue;
-		pmid_to_url( f, i, "URL", &s );
-		if ( s.len )
-			fprintf( fp, "UR  - %s\n", s.data );
+	int lstatus;
+	slist types;
+
+	lstatus = slist_init_valuesc( &types, "URL", "DOI", "PMID", "PMC", "ARXIV", "JSTOR", "MRNUMBER", NULL );
+	if ( lstatus!=SLIST_OK ) {
+		*status = BIBL_ERR_MEMERR;
+		return;
 	}
-	newstr_free( &s );
+
+	*status = urls_merge_and_add( in, LEVEL_ANY, out, "UR", LEVEL_MAIN, &types );
+
+	slist_free( &types );
+
 }
 
 static void
-output_arxiv( FILE *fp, fields *f )
+append_thesishint( int type, fields *out, int *status )
 {
-	newstr s;
-	int i;
-	newstr_init( &s );
-	for ( i=0; i<fields_num( f ); ++i ) {
-		if ( !fields_match_tag( f, i, "ARXIV" ) ) continue;
-		arxiv_to_url( f, i, "URL", &s );
-		if ( s.len )
-			fprintf( fp, "UR  - %s\n", s.data );
+	int fstatus;
+
+	if ( type==TYPE_MASTERSTHESIS ) {
+		fstatus = fields_add( out, "U1", "Masters thesis", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	}
-	newstr_free( &s );
-}
 
-static void
-output_jstor( FILE *fp, fields *f )
-{
-	newstr s;
-	int i;
-	newstr_init( &s );
-	for ( i=0; i<fields_num( f ); ++i ) {
-		if ( !fields_match_tag( f, i, "JSTOR" ) ) continue;
-		jstor_to_url( f, i, "URL", &s );
-		if ( s.len )
-			fprintf( fp, "UR  - %s\n", s.data );
+	else if ( type==TYPE_PHDTHESIS ) {
+		fstatus = fields_add( out, "U1", "Ph.D. thesis", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
 	}
-	newstr_free( &s );
-}
 
-static void
-output_thesishint( FILE *fp, int type )
-{
-	if ( type==TYPE_MASTERSTHESIS )
-		fprintf( fp, "%s  - %s\n", "U1", "Masters thesis" );
-	else if ( type==TYPE_PHDTHESIS )
-		fprintf( fp, "%s  - %s\n", "U1", "Ph.D. thesis" );
-	else if ( type==TYPE_DIPLOMATHESIS )
-		fprintf( fp, "%s  - %s\n", "U1", "Diploma thesis" );
-	else if ( type==TYPE_DOCTORALTHESIS )
-		fprintf( fp, "%s  - %s\n", "U1", "Doctoral thesis" );
-	else if ( type==TYPE_HABILITATIONTHESIS )
-		fprintf( fp, "%s  - %s\n", "U1", "Habilitation thesis" );
+	else if ( type==TYPE_DIPLOMATHESIS ) {
+		fstatus = fields_add( out, "U1", "Diploma thesis", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+
+	else if ( type==TYPE_DOCTORALTHESIS ) {
+		fstatus = fields_add( out, "U1", "Doctoral thesis", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
+
+	else if ( type==TYPE_HABILITATIONTHESIS ) {
+		fstatus = fields_add( out, "U1", "Habilitation thesis", LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 }
 
 static int
 is_uri_scheme( char *p )
 {
-	char *scheme[] = { "http:", "file:", "ftp:", "git:", "gopher:" };
+	char *scheme[] = { "http:", "https:", "file:", "ftp:", "git:", "gopher:" };
 	int i, len, nschemes = sizeof( scheme ) / sizeof( scheme[0] );
 	for ( i=0; i<nschemes; ++i ) {
 		len = strlen( scheme[i] );
@@ -541,120 +561,164 @@
 
 
 static void
-output_file( FILE *fp, fields *f, char *tag, char *ristag, int level )
+append_file( fields *in, char *tag, char *ristag, int level, fields *out, int *status )
 {
+	vplist_index i;
+	str filename;
+	int fstatus;
 	vplist a;
 	char *fl;
-	int i;
+
+	str_init( &filename );
 	vplist_init( &a );
-	fields_findv_each( f, level, FIELDS_CHRP, &a, tag );
+	fields_findv_each( in, level, FIELDS_CHRP, &a, tag );
 	for ( i=0; i<a.n; ++i ) {
-		fprintf( fp, "%s  - ", ristag );
 		fl = ( char * ) vplist_get( &a, i );
-		if ( !is_uri_scheme( fl ) )
-			fprintf( fp, "file:" );
-		fprintf( fp, "%s\n", fl );
+		str_empty( &filename );
+		if ( !is_uri_scheme( fl ) ) str_strcatc( &filename, "file:" );
+		str_strcatc( &filename, fl );
+		if ( str_memerr( &filename ) ) { *status = BIBL_ERR_MEMERR; goto out; }
+		fstatus = fields_add( out, ristag, str_cstr( &filename ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) { *status = BIBL_ERR_MEMERR; goto out; }
 	}
+out:
 	vplist_free( &a );
+	str_free( &filename );
 }
 
 static void
-output_easy( FILE *fp, fields *f, char *tag, char *ristag, int level )
+append_easy( fields *in, char *tag, char *ristag, int level, fields *out, int *status )
 {
-	char *value = fields_findv( f, level, FIELDS_CHRP, tag );
-	if ( value ) fprintf( fp, "%s  - %s\n", ristag, value );
+	char *value;
+	int fstatus;
+
+	value = fields_findv( in, level, FIELDS_CHRP, tag );
+	if ( value ) {
+		fstatus = fields_add( out, ristag, value, LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 }
 
 static void
-output_easyall( FILE *fp, fields *f, char *tag, char *ristag, int level )
+append_easyall( fields *in, char *tag, char *ristag, int level, fields *out, int *status )
 {
+	vplist_index i;
+	int fstatus;
 	vplist a;
-	int i;
+
 	vplist_init( &a );
-	fields_findv_each( f, level, FIELDS_CHRP, &a, tag );
-	for ( i=0; i<a.n; ++i )
-		fprintf( fp, "%s  - %s\n", ristag, (char *) vplist_get( &a, i ) );
+	fields_findv_each( in, level, FIELDS_CHRP, &a, tag );
+	for ( i=0; i<a.n; ++i ) {
+		fstatus = fields_add( out, ristag, (char *) vplist_get( &a, i ), LEVEL_MAIN );
+		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
+	}
 	vplist_free( &a );
 }
 
 static void
-output_allpeople( FILE *fp, fields *f, int type )
+append_allpeople( fields *in, int type, fields *out, int *status )
 {
-	output_people(  fp, f, "AUTHOR",      "AU", LEVEL_MAIN   );
-	output_easyall( fp, f, "AUTHOR:CORP", "AU", LEVEL_MAIN   );
-	output_easyall( fp, f, "AUTHOR:ASIS", "AU", LEVEL_MAIN   );
-	output_people(  fp, f, "AUTHOR",      "A2", LEVEL_HOST   );
-	output_easyall( fp, f, "AUTHOR:CORP", "A2", LEVEL_HOST   );
-	output_easyall( fp, f, "AUTHOR:ASIS", "A2", LEVEL_HOST   );
-	output_people(  fp, f, "AUTHOR",      "A3", LEVEL_SERIES );
-	output_easyall( fp, f, "AUTHOR:CORP", "A3", LEVEL_SERIES );
-	output_easyall( fp, f, "AUTHOR:ASIS", "A3", LEVEL_SERIES );
-	output_people(  fp, f, "EDITOR",      "ED", LEVEL_MAIN   );
-	output_easyall( fp, f, "EDITOR:CORP", "ED", LEVEL_MAIN   );
-	output_easyall( fp, f, "EDITOR:ASIS", "ED", LEVEL_MAIN   );
-	if ( type_is_element( type ) ) {
-		output_people(  fp, f, "EDITOR",      "ED", LEVEL_HOST   );
-		output_easyall( fp, f, "EDITOR:CORP", "ED", LEVEL_HOST   );
-		output_easyall( fp, f, "EDITOR:ASIS", "ED", LEVEL_HOST   );
+	append_people ( in, "AUTHOR",      "AU", LEVEL_MAIN,   out, status );
+	append_easyall( in, "AUTHOR:CORP", "AU", LEVEL_MAIN,   out, status );
+	append_easyall( in, "AUTHOR:ASIS", "AU", LEVEL_MAIN,   out, status );
+	append_people ( in, "AUTHOR",      "A2", LEVEL_HOST,   out, status );
+	append_easyall( in, "AUTHOR:CORP", "A2", LEVEL_HOST,   out, status );
+	append_easyall( in, "AUTHOR:ASIS", "A2", LEVEL_HOST,   out, status );
+	append_people ( in, "AUTHOR",      "A3", LEVEL_SERIES, out, status );
+	append_easyall( in, "AUTHOR:CORP", "A3", LEVEL_SERIES, out, status );
+	append_easyall( in, "AUTHOR:ASIS", "A3", LEVEL_SERIES, out, status );
+	append_people ( in, "EDITOR",      "ED", LEVEL_MAIN,   out, status );
+	append_easyall( in, "EDITOR:CORP", "ED", LEVEL_MAIN,   out, status );
+	append_easyall( in, "EDITOR:ASIS", "ED", LEVEL_MAIN,   out, status );
+	if ( type_is_element[ type ] ) {
+		append_people ( in, "EDITOR",      "ED", LEVEL_HOST, out, status );
+		append_easyall( in, "EDITOR:CORP", "ED", LEVEL_HOST, out, status );
+		append_easyall( in, "EDITOR:ASIS", "ED", LEVEL_HOST, out, status );
 	} else {
-		output_people(  fp, f, "EDITOR",      "A3", LEVEL_HOST   );
-		output_easyall( fp, f, "EDITOR:CORP", "A3", LEVEL_HOST   );
-		output_easyall( fp, f, "EDITOR:ASIS", "A3", LEVEL_HOST   );
+		append_people ( in, "EDITOR",      "A3", LEVEL_HOST, out, status );
+		append_easyall( in, "EDITOR:CORP", "A3", LEVEL_HOST, out, status );
+		append_easyall( in, "EDITOR:ASIS", "A3", LEVEL_HOST, out, status );
 	}
-	output_people(  fp, f, "EDITOR",      "A3", LEVEL_SERIES );
-	output_easyall( fp, f, "EDITOR:CORP", "A3", LEVEL_SERIES );
-	output_easyall( fp, f, "EDITOR:ASIS", "A3", LEVEL_SERIES );
+	append_people ( in, "EDITOR",      "A3", LEVEL_SERIES, out, status );
+	append_easyall( in, "EDITOR:CORP", "A3", LEVEL_SERIES, out, status );
+	append_easyall( in, "EDITOR:ASIS", "A3", LEVEL_SERIES, out, status );
 }
 
-void
-risout_write( fields *f, FILE *fp, param *p, unsigned long refnum )
+static void
+output( FILE *fp, fields *out )
 {
-	int type;
-	type = get_type( f, p );
-	output_type( fp, type, p );
-
-	output_allpeople( fp, f, type );
-
-	output_date( fp, f );
+	char *tag, *value;
+	int i;
 
-	output_alltitles( fp, f, type );
+	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 );
+	}
 
-	output_pages( fp, f );
-	output_easy( fp, f, "VOLUME",             "VL", LEVEL_ANY );
-	output_easy( fp, f, "ISSUE",              "IS", LEVEL_ANY );
-	output_easy( fp, f, "NUMBER",             "IS", LEVEL_ANY );
-	output_easy( fp, f, "EDITION",            "ET", LEVEL_ANY );
-	output_easy( fp, f, "NUMVOLUMES",         "NV", LEVEL_ANY );
-	output_easy( fp, f, "AUTHORADDRESS",      "AD", LEVEL_ANY );
-	output_easy( fp, f, "PUBLISHER",          "PB", LEVEL_ANY );
-	output_easy( fp, f, "DEGREEGRANTOR",      "PB", LEVEL_ANY );
-	output_easy( fp, f, "DEGREEGRANTOR:ASIS", "PB", LEVEL_ANY );
-	output_easy( fp, f, "DEGREEGRANTOR:CORP", "PB", LEVEL_ANY );
-	output_easy( fp, f, "ADDRESS",            "CY", LEVEL_ANY );
-	output_keywords( fp, f );
-	output_easy( fp, f, "ABSTRACT",           "AB", LEVEL_ANY );
-	output_easy( fp, f, "CALLNUMBER",         "CN", LEVEL_ANY );
-	output_easy( fp, f, "ISSN",               "SN", LEVEL_ANY );
-	output_easy( fp, f, "ISBN",               "SN", LEVEL_ANY );
-	output_easyall( fp, f, "URL",             "UR", LEVEL_ANY );
-	output_easyall( fp, f, "DOI",             "DO", LEVEL_ANY );
-	output_file(    fp, f, "FILEATTACH",      "L1", LEVEL_ANY );
-	output_file(    fp, f, "FIGATTACH",       "L4", LEVEL_ANY );
-	output_easy( fp, f, "CAPTION",            "CA", LEVEL_ANY );
-	output_pmid( fp, f );
-	output_arxiv( fp, f );
-	output_jstor( fp, f );
-	output_easy( fp, f, "LANGUAGE",           "LA", LEVEL_ANY );
-	output_easy( fp, f, "NOTES",              "N1", LEVEL_ANY );
-	output_easy( fp, f, "REFNUM",             "ID", LEVEL_ANY );
-	output_thesishint( fp, type );
 	fprintf( fp, "ER  - \n" );
 	fflush( fp );
 }
 
-void
+static int
+append_data( fields *in, param *p, fields *out )
+{
+	int type, status = BIBL_OK;
+
+	type = get_type( in, p );
+
+	append_type      ( type, p, out, &status );
+	append_allpeople ( in, type, out, &status );
+	append_date      ( in, out, &status );
+	append_alltitles ( in, type, out, &status );
+	append_pages     ( in, out, &status );
+	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, "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, "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 );
+	append_easy      ( in, "DEGREEGRANTOR:CORP", "PB", LEVEL_ANY, out, &status );
+	append_easy      ( in, "ADDRESS",            "CY", LEVEL_ANY, out, &status );
+	append_keywords  ( in, out, &status );
+	append_easy      ( in, "ABSTRACT",           "AB", LEVEL_ANY, out, &status );
+	append_easy      ( in, "CALLNUMBER",         "CN", LEVEL_ANY, out, &status );
+	append_easy      ( in, "ISSN",               "SN", LEVEL_ANY, out, &status );
+	append_easy      ( in, "ISBN",               "SN", LEVEL_ANY, out, &status );
+	append_file      ( in, "FILEATTACH",         "L1", LEVEL_ANY, out, &status );
+	append_file      ( in, "FIGATTACH",          "L4", LEVEL_ANY, out, &status );
+	append_easy      ( in, "CAPTION",            "CA", LEVEL_ANY, out, &status );
+	append_urls      ( in, out, &status );
+	append_easyall   ( in, "DOI",                "DO", LEVEL_ANY, out, &status );
+	append_easy      ( in, "LANGUAGE",           "LA", LEVEL_ANY, out, &status );
+	append_easy      ( in, "NOTES",              "N1", LEVEL_ANY, out, &status );
+	append_easy      ( in, "REFNUM",             "ID", LEVEL_ANY, out, &status );
+	append_thesishint( type, out, &status );
+
+	return status;
+}
+
+static int
+risout_write( fields *in, 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 );
+
+	return status;
+}
+
+static void
 risout_writeheader( FILE *outptr, param *p )
 {
 	if ( p->utf8bom ) utf8_writebom( outptr );
 }
-
diff --git a/bibutils/risout.h b/bibutils/risout.h
deleted file mode 100644
--- a/bibutils/risout.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * risout.h
- *
- * Copyright (c) Chris Putnam 2005-2013
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef RISOUT_H
-#define RISOUT_H
-
-#include <stdio.h>
-#include "bibutils.h"
-
-extern void risout_write( fields *info, FILE *fp, param *p,
-		unsigned long refnum );
-extern void risout_writeheader( FILE *outptr, param *p );
-
-extern void risout_initparams( param *p, const char *progname );
-
-
-#endif
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-2013
+ * Copyright (c) Chris Putnam 2003-2017
  *
  * Source code released under the GPL version 2
  *
@@ -34,13 +34,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Name of Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -62,13 +62,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Resarch Notes -> Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Resarch Notes -> Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -80,11 +80,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated? */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated? */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 };
 
 static lookups article[] = {
@@ -108,13 +108,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_HOST },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -136,13 +136,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_HOST },   /* Publisher */
-	{ "PY", "PARTYEAR",     DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "PARTDATE:YEAR",DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_HOST },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -154,12 +154,12 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "PARTYEAR",     DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "PARTMONTH",    SIMPLE,  LEVEL_MAIN },   /* Access Date */
-	{ "  ", "INTERNAL_TYPE|ARTICLE",           ALWAYS, LEVEL_MAIN },
+	{ "Y1", "PARTDATE:YEAR",DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "PARTDATE:MONTH",SIMPLE, LEVEL_MAIN },   /* Access Date */
+	{ "  ", "INTERNAL_TYPE|ARTICLE",  ALWAYS, LEVEL_MAIN },
 	{ "  ", "ISSUANCE|continuing",    ALWAYS, LEVEL_HOST },
 	{ "  ", "RESOURCE|text",          ALWAYS, LEVEL_MAIN },
 	{ "  ", "GENRE|journal article",  ALWAYS, LEVEL_MAIN },
@@ -189,13 +189,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_HOST },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -217,13 +217,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_HOST },   /* Publisher */
-	{ "PY", "PARTYEAR",     DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "PARTDATE:YEAR",DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_HOST },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -235,11 +235,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "PARTYEAR",     DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "PARTMONTH",    SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "PARTDATE:YEAR",DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "PARTDATE:MONTH",SIMPLE, LEVEL_MAIN },   /* Access Date */
 	{ "  ", "ISSUANCE|continuing",    ALWAYS, LEVEL_HOST },
 	{ "  ", "RESOURCE|text",          ALWAYS, LEVEL_MAIN },
 	{ "  ", "GENRE|periodical",       ALWAYS, LEVEL_HOST },
@@ -267,13 +267,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_HOST },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -295,13 +295,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_HOST },   /* Publisher */
-	{ "PY", "PARTYEAR",     DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "PARTDATE:YEAR",DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_HOST },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -313,15 +313,15 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "PARTYEAR",     DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "PARTMONTH",    SIMPLE,  LEVEL_MAIN },   /* Access Date */
-	{ "  ", "INTERNAL_TYPE|NEWSARTICLE",       ALWAYS, LEVEL_MAIN },
-	{ "  ", "ISSUANCE|continuing",    ALWAYS, LEVEL_HOST },
-	{ "  ", "RESOURCE|text",          ALWAYS, LEVEL_MAIN },
-	{ "  ", "GENRE|newspaper",        ALWAYS, LEVEL_HOST }
+	{ "Y1", "PARTDATE:YEAR",DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "PARTDATE:MONTH",SIMPLE, LEVEL_MAIN },   /* Access Date */
+	{ "  ", "INTERNAL_TYPE|NEWSARTICLE", ALWAYS, LEVEL_MAIN },
+	{ "  ", "ISSUANCE|continuing",       ALWAYS, LEVEL_HOST },
+	{ "  ", "RESOURCE|text",             ALWAYS, LEVEL_MAIN },
+	{ "  ", "GENRE|newspaper",           ALWAYS, LEVEL_HOST }
 };
 
 static lookups book[] = {
@@ -346,13 +346,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -374,13 +374,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title -- here abbreviated title for series*/
@@ -392,14 +392,14 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
-	{ "  ", "ISSUANCE|monographic",  ALWAYS, LEVEL_MAIN },
-	{ "  ", "GENRE|book",    ALWAYS, LEVEL_MAIN },
-	{ "  ", "RESOURCE|text", ALWAYS, LEVEL_MAIN }
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "  ", "ISSUANCE|monographic",   ALWAYS, LEVEL_MAIN },
+	{ "  ", "GENRE|book",             ALWAYS, LEVEL_MAIN },
+	{ "  ", "RESOURCE|text",          ALWAYS, LEVEL_MAIN }
 };
 
 static lookups inbook[] = {
@@ -424,13 +424,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_HOST },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_HOST },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_HOST },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_HOST },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_HOST },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_HOST },   /* Number */
@@ -452,13 +452,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_HOST },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_HOST },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_HOST },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_HOST },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_SERIES }, /* 'Secondary' Title -- here abbreviated title for series*/
@@ -470,11 +470,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_HOST },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_HOST },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_HOST },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_HOST },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_HOST },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_HOST },   /* Access Date */
 	{ "  ", "GENRE|book chapter",   ALWAYS, LEVEL_MAIN },
 	{ "  ", "GENRE|book",           ALWAYS, LEVEL_HOST },
 	{ "  ", "ISSUANCE|monographic", ALWAYS, LEVEL_HOST },
@@ -502,13 +502,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -530,13 +530,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_HOST },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_HOST },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_HOST },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "BT", "TITLE",        SIMPLE,  LEVEL_HOST },   /* Book Title */
@@ -549,12 +549,12 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_HOST },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_HOST },   /* Access Date */
-	{ "  ", "INTERNAL_TYPE|CONFERENCE",       ALWAYS, LEVEL_MAIN },
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_HOST },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_HOST },   /* Access Date */
+	{ "  ", "INTERNAL_TYPE|CONFERENCE",     ALWAYS, LEVEL_MAIN },
 	{ "  ", "RESOURCE|text",                ALWAYS, LEVEL_MAIN },
 	{ "  ", "GENRE|conference publication", ALWAYS, LEVEL_HOST }
 };
@@ -581,13 +581,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -609,13 +609,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -627,12 +627,12 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
-	{ "  ", "INTERNAL_TYPE|STATUTE",          ALWAYS, LEVEL_MAIN },
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "  ", "INTERNAL_TYPE|STATUTE", ALWAYS, LEVEL_MAIN },
 	{ "  ", "RESOURCE|text",         ALWAYS, LEVEL_MAIN },
 	{ "  ", "GENRE|legislation",     ALWAYS, LEVEL_MAIN }
 };
@@ -659,13 +659,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -687,13 +687,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -705,11 +705,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 	{ "  ", "INTERNAL_TYPE|HEARING", ALWAYS, LEVEL_MAIN },
 	{ "  ", "GENRE|hearing",         ALWAYS, LEVEL_MAIN }
 };
@@ -736,13 +736,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -764,13 +764,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -782,11 +782,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 	{ "  ", "INTERNAL_TYPE|CASE",              ALWAYS, LEVEL_MAIN },
 	{ "  ", "GENRE|legal case and case notes", ALWAYS, LEVEL_MAIN }
 };
@@ -813,13 +813,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -841,13 +841,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -859,11 +859,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 	{ "  ", "INTERNAL_TYPE|ARTICLE", ALWAYS, LEVEL_MAIN },
 	{ "  ", "GENRE|communication",   ALWAYS, LEVEL_MAIN }
 };
@@ -890,13 +890,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -918,13 +918,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -936,11 +936,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 	{ "  ", "INTERNAL_TYPE|THESIS",  ALWAYS,  LEVEL_MAIN },
 	{ "  ", "RESOURCE|text",         ALWAYS,  LEVEL_MAIN },
 	{ "  ", "GENRE|thesis",          ALWAYS,  LEVEL_MAIN },
@@ -968,13 +968,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -996,13 +996,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -1014,11 +1014,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 	{ "  ", "INTERNAL_TYPE|REPORT",  ALWAYS,  LEVEL_MAIN },
 	{ "  ", "RESOURCE|text",         ALWAYS,  LEVEL_MAIN },
 	{ "  ", "GENRE|report",          ALWAYS,  LEVEL_MAIN }
@@ -1046,13 +1046,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -1074,13 +1074,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -1092,11 +1092,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 	{ "  ", "GENRE|abstract or summary", ALWAYS,  LEVEL_MAIN }
 };
 
@@ -1122,13 +1122,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -1150,13 +1150,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -1168,11 +1168,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 	{ "  ", "RESOURCE|software, multimedia", ALWAYS, LEVEL_MAIN }
 };
 
@@ -1198,13 +1198,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -1226,13 +1226,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -1244,11 +1244,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 	{ "  ", "RESOURCE|text", ALWAYS, LEVEL_MAIN },
 	{ "  ", "GENRE|patent",  ALWAYS, LEVEL_MAIN }
 };
@@ -1275,13 +1275,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -1303,13 +1303,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -1321,11 +1321,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 	{ " ",  "RESOURCE|software, multimedia", ALWAYS, LEVEL_MAIN },
 	{ " ",  "GENRE|electronic",              ALWAYS, LEVEL_MAIN },
 };
@@ -1352,13 +1352,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -1380,13 +1380,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -1398,11 +1398,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 	{ " ",  "RESOURCE|text",  ALWAYS, LEVEL_MAIN },
 	{ " ",  "GENRE|pamphlet", ALWAYS, LEVEL_MAIN },
 };
@@ -1429,13 +1429,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Name of Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -1457,13 +1457,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Resarch Notes -> Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Resarch Notes -> Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -1475,11 +1475,11 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated? */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated? */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
 	{ " ",  "RESOURCE|cartographic",   ALWAYS, LEVEL_MAIN },
 	{ " ",  "GENRE|map",               ALWAYS, LEVEL_MAIN }
 };
@@ -1506,13 +1506,13 @@
 	{ "CP", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CT", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "CY", "ADDRESS",      SIMPLE,  LEVEL_MAIN },   /* Place Published */
-	{ "DA", "YEAR",         DATE,    LEVEL_MAIN },   /* Date */
+	{ "DA", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Date */
 	{ "DB", "DATABASE",     SIMPLE,  LEVEL_MAIN },   /* Database */
 	{ "DI", "DOI",          DOI,     LEVEL_MAIN },   /* Deprecated? */
 	{ "DO", "DOI",          DOI,     LEVEL_MAIN },   /* DOI */
 	{ "DP", "DATABASEPROV", SIMPLE,  LEVEL_MAIN },   /* Database Provider */
 	{ "ED", "EDITOR",       PERSON,  LEVEL_MAIN },   /* Deprecated? */
-	{ "EP", "PAGEEND",      SIMPLE,  LEVEL_MAIN },   /* End Page */
+	{ "EP", "PAGES:STOP",   SIMPLE,  LEVEL_MAIN },   /* End Page */
 	{ "ET", "EDITION",      SIMPLE,  LEVEL_MAIN },   /* Edition */
 	{ "ID", "REFNUM",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
 	{ "IS", "ISSUE",        SIMPLE,  LEVEL_MAIN },   /* Number */
@@ -1534,13 +1534,13 @@
 	{ "NV", "NUMVOLUMES",   SIMPLE,  LEVEL_MAIN },   /* Number of Volumes */
 	{ "OP", "ORIGPUB",      SIMPLE,  LEVEL_MAIN },   /* Original Publication */
 	{ "PB", "PUBLISHER",    SIMPLE,  LEVEL_MAIN },   /* Publisher */
-	{ "PY", "YEAR",         DATE,    LEVEL_MAIN },   /* Year */
+	{ "PY", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Year */
 	{ "RI", "REVIEWEDITEM", SIMPLE,  LEVEL_MAIN },   /* Reviewed Item */
-	{ "RN", "NOTES",        SIMPLE,  LEVEL_MAIN },   /* Research Notes */
+	{ "RN", "NOTES",        NOTES,   LEVEL_MAIN },   /* Research Notes */
 	{ "RP", "REPRINTSTATUS",SIMPLE,  LEVEL_MAIN },   /* Reprint Edition */
 	{ "SE", "SECTION",      SIMPLE,  LEVEL_MAIN },   /* Section */
 	{ "SN", "SERIALNUMBER", SERIALNO,LEVEL_MAIN },   /* ISBN/ISSN */
-	{ "SP", "PAGESTART",    SIMPLE,  LEVEL_MAIN },   /* Start Page */
+	{ "SP", "PAGES:START",  SIMPLE,  LEVEL_MAIN },   /* Start Page */
 	{ "ST", "SHORTTITLE",   SIMPLE,  LEVEL_MAIN },   /* Short Title */
 	{ "T1", "TITLE",        TITLE,   LEVEL_MAIN },   /* Deprecated? */
 	{ "T2", "SHORTTITLE",   SIMPLE,  LEVEL_HOST },   /* 'Secondary' Title */
@@ -1552,13 +1552,13 @@
 	{ "U3", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U4", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
 	{ "U5", "NOTES",        NOTES,   LEVEL_MAIN },   /* 'User' - Deprecated? */
-	{ "UR", "URL",          SIMPLE,  LEVEL_MAIN },   /* URL */
+	{ "UR", "URL",          URL,     LEVEL_MAIN },   /* URL */
 	{ "VL", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Volume */
 	{ "VO", "VOLUME",       SIMPLE,  LEVEL_MAIN },   /* Deprecated? */
-	{ "Y1", "YEAR",         DATE,    LEVEL_MAIN },   /* Deprecated */
-	{ "Y2", "MONTH",        SIMPLE,  LEVEL_MAIN },   /* Access Date */
-	{ " ",         "RESOURCE|text",   ALWAYS, LEVEL_MAIN },
-	{ " ",         "GENRE|unpublished",      ALWAYS, LEVEL_MAIN }
+	{ "Y1", "DATE:YEAR",    DATE,    LEVEL_MAIN },   /* Deprecated */
+	{ "Y2", "DATE:MONTH",   SIMPLE,  LEVEL_MAIN },   /* Access Date */
+	{ " ",  "RESOURCE|text",     ALWAYS, LEVEL_MAIN },
+	{ " ",  "GENRE|unpublished", ALWAYS, LEVEL_MAIN }
 };
 
 #define ORIG(a) ( &(a[0]) )
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-2014
+ * Copyright (c) Chris Putnam 2005-2017
  *
  * 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,7 +1,7 @@
 /*
  * serialno.h
  *
- * Copyright (c) Chris Putnam 2005-2014
+ * Copyright (c) Chris Putnam 2005-2017
  *
  * Source code released under the GPL version 2
  *
diff --git a/bibutils/slist.c b/bibutils/slist.c
new file mode 100644
--- /dev/null
+++ b/bibutils/slist.c
@@ -0,0 +1,865 @@
+/*
+ * slist.c
+ *
+ * version: 2017-07-04
+ *
+ * Copyright (c) Chris Putnam 2004-2017
+ *
+ * Source code released under the GPL version 2
+ *
+ * Implements a simple managed array of strs.
+ *
+ */
+#include "slist.h"
+
+/* Do not use asserts in VPLIST_NOASSERT defined */
+#ifdef VPLIST_NOASSERT
+#define NDEBUG
+#endif
+#include <assert.h>
+
+#define SLIST_MINALLOC (20)
+
+#define SLIST_EXACT_SIZE  (0)
+#define SLIST_DOUBLE_SIZE (1)
+
+/*
+ * returns 1 if n is valid string in slist
+ */
+static inline int
+slist_valid_num( slist *a, slist_index n )
+{
+	if ( n < 0 || n >= a->n ) return 0;
+	return 1;
+}
+
+void
+slist_init( slist *a  )
+{
+	assert( a );
+
+	a->strs = NULL;
+	a->max = 0;
+	a->n = 0;
+	a->sorted = 1;
+}
+
+int
+slist_init_values( slist *a, ... )
+{
+	int status = SLIST_OK;
+	va_list ap;
+	str *s, *t;
+
+	slist_init( a );
+
+	va_start( ap, a );
+	do {
+		s = va_arg( ap, str * );
+		if ( s ) {
+			t = slist_add( a, s );
+			if ( !t ) {
+				status = SLIST_ERR_MEMERR;
+				goto out;
+			}
+		}
+	} while ( s );
+out:
+	va_end( ap );
+
+	return status;
+}
+
+int
+slist_init_valuesc( slist *a, ... )
+{
+	int status = SLIST_OK;
+	va_list ap;
+	char *s;
+	str *t;
+
+	slist_init( a );
+
+	va_start( ap, a );
+	do {
+		s = va_arg( ap, char * );
+		if ( s ) {
+			t = slist_addc( a, s );
+			if ( !t ) {
+				status = SLIST_ERR_MEMERR;
+				goto out;
+			}
+		}
+	} while ( s );
+out:
+	va_end( ap );
+
+	return status;
+}
+
+void
+slist_empty( slist *a )
+{
+	slist_index i;
+
+	assert( a );
+
+	for ( i=0; i<a->max; ++i )
+		str_empty( &(a->strs[i]) );
+
+	a->n = 0;
+	a->sorted = 1;
+}
+
+void
+slist_free( slist *a )
+{
+	slist_index i;
+
+	assert( a );
+
+	for ( i=0; i<a->max; ++i )
+		str_free( &(a->strs[i]) );
+
+	free( a->strs );
+	slist_init( a );
+}
+
+slist *
+slist_new( void )
+{
+	slist *a;
+
+	a = ( slist * ) malloc( sizeof ( slist ) );
+	if ( a ) slist_init( a );
+
+	return a;
+}
+
+void
+slist_delete( slist *a )
+{
+	assert( a );
+
+	slist_free( a );
+	free( a );
+}
+
+void
+slist_deletev( void *v )
+{
+	slist_delete( (slist*) v );
+}
+
+void
+slist_swap( slist *a, slist_index n1, slist_index n2 )
+{
+	assert( a );
+
+	if ( slist_valid_num( a, n1 ) && slist_valid_num( a, n2 ) )
+		str_swapstrings( &(a->strs[n1]), &(a->strs[n2]) );
+}
+
+static int
+slist_comp( const void *v1, const void *v2 )
+{
+	str *s1 = ( str *) v1;
+	str *s2 = ( str *) v2;
+	if ( !s1->len && !s2->len ) return 0;
+	else if ( !s1->len ) return -1;
+	else if ( !s2->len ) return 1;
+	else return str_strcmp( s1, s2 );
+}
+
+static int
+slist_comp_step( slist *a, slist_index n1, slist_index n2 )
+{
+	return slist_comp( (const void*) &(a->strs[n1]), (const void*) &(a->strs[n2]) );
+}
+
+static str *
+slist_set_cleanup( slist *a, slist_index n )
+{
+	if ( str_memerr( &(a->strs[n]) ) ) return NULL;
+	if ( a->sorted ) {
+		if ( n>0 && slist_comp_step( a, n-1, n )>0 )
+			a->sorted = 0;
+	}
+	if ( a->sorted ) {
+		if ( n<a->n-1 && slist_comp_step( a, n, n+1 )>0 )
+			a->sorted = 0;
+	}
+	return &(a->strs[n]);
+}
+
+str *
+slist_setc( slist *a, slist_index n, const char *s )
+{
+	assert( a );
+	assert( s );
+
+	if ( !slist_valid_num( a, n ) ) return NULL;
+	str_strcpyc( &(a->strs[n]), s );
+	return slist_set_cleanup( a, n );
+}
+
+str *
+slist_set( slist *a, slist_index n, str *s )
+{
+	assert( s );
+
+	return slist_setc( a, n, str_cstr( s ) );
+}
+
+/*
+ * return pointer to str 'n'
+ */
+str *
+slist_str( slist *a, slist_index n )
+{
+	assert( a );
+
+	if ( !slist_valid_num( a, n ) ) return NULL;
+	else return &(a->strs[n]);
+}
+
+/*
+ * return pointer to C string 'n'
+ *
+ * So long as the index is a valid number ensure
+ * that a pointer is returned even if the newstr isn't
+ * allocated. Only return NULL if the index
+ * is invalid. Thus we can convert loops like:
+ *
+ * for ( i=0; i<a->n; ++i ) {
+ *      p = slist_cstr( a, i );
+ *      if ( p==NULL ) continue; // empty string
+ *      ...
+ * }
+ *
+ * to
+ *
+ * i = 0;
+ * while ( ( p = slist_cstr( a, i ) ) ) {
+ *      ...
+ *      i++;
+ * }
+ *
+ */
+char *
+slist_cstr( slist *a, slist_index n )
+{
+	static char empty[] = "";
+	char *p;
+
+	assert( a );
+
+	if ( !slist_valid_num( a, n ) ) return NULL;
+	p = str_cstr( &(a->strs[n]) );
+	if ( p ) return p;
+	else return empty;
+}
+
+static inline int
+slist_alloc( slist *a, slist_index alloc )
+{
+	slist_index i;
+
+	a->strs = ( str* ) malloc( sizeof( str ) * alloc );
+	if ( !(a->strs) ) return SLIST_ERR_MEMERR;
+
+	a->max = alloc;
+	a->n   = 0;
+
+	for ( i=0; i<alloc; ++i )
+		str_init( &(a->strs[i]) );
+
+	return SLIST_OK;
+}
+
+static inline int
+slist_realloc( slist *a, slist_index alloc )
+{
+	slist_index i;
+	str *more;
+
+	more = ( str* ) realloc( a->strs, sizeof( str ) * alloc );
+	if ( !more ) return SLIST_ERR_MEMERR;
+
+	a->strs = more;
+
+	for ( i=a->max; i<alloc; ++i )
+		str_init( &(a->strs[i]) );
+
+	a->max = alloc;
+
+	return SLIST_OK;
+}
+
+#define SLIST_EXACT_SIZE  (0)
+#define SLIST_DOUBLE_SIZE (1)
+
+static int
+slist_ensure_space( slist *a, slist_index n, int mode )
+{
+	int status = SLIST_OK;
+	int alloc = n;
+
+	if ( a->max==0 ) {
+		if ( mode == SLIST_DOUBLE_SIZE && alloc < SLIST_MINALLOC ) alloc = SLIST_MINALLOC;
+		status = slist_alloc( a, alloc );
+	}
+
+	else if ( a->max < n ) {
+		if ( mode == SLIST_DOUBLE_SIZE && alloc < a->max * 2 ) alloc = a->max * 2;
+		status = slist_realloc( a, alloc );
+	}
+
+	return status;
+}
+
+str *
+slist_addvp( slist *a, unsigned char mode, void *vp )
+{
+	str *s = NULL;
+	int status;
+
+	status = slist_ensure_space( a, a->n+1, SLIST_DOUBLE_SIZE );
+
+	if ( status==SLIST_OK ) {
+
+		s = &( a->strs[a->n] );
+
+		if ( mode==SLIST_CHR )
+			str_strcpyc( s, (const char*) vp );
+		else
+			str_strcpy( s, (str*) vp );
+
+		if ( str_memerr( s ) ) return NULL;
+		a->n++;
+		if ( a->sorted && a->n > 1 ) {
+			if ( slist_comp_step( a, a->n-2, a->n-1 ) > 0 )
+				a->sorted = 0;
+		}
+
+	}
+
+	return s;
+}
+str *
+slist_addc( slist *a, const char *s )
+{
+	return slist_addvp( a, SLIST_CHR, (void*)s );
+}
+str *
+slist_add( slist *a, str *s )
+{
+	return slist_addvp( a, SLIST_STR, (void*)s );
+}
+
+str *
+slist_addvp_unique( slist *a, unsigned char mode, void *vp )
+{
+	str *s;
+	int n;
+
+	if ( mode==SLIST_CHR )
+		n = slist_findc( a, (const char*) vp );
+	else
+		n = slist_find( a, (str*) vp );
+
+	if ( slist_wasfound( a, n ) )
+		s = &( a->strs[n] );
+	else
+		s = slist_addvp( a, mode, vp );
+
+	return s;
+}
+str *
+slist_addc_unique( slist *a, const char *s )
+{
+	return slist_addvp_unique( a, SLIST_CHR, (void*)s );
+}
+str *
+slist_add_unique( slist *a, str *s )
+{
+	return slist_addvp_unique( a, SLIST_STR, (void*)s );
+}
+
+int
+slist_addvp_all( slist *a, unsigned char mode, ... )
+{
+	int ret = SLIST_OK;
+	va_list ap;
+	void *v;
+	str *s;
+
+	va_start( ap, mode );
+
+	do {
+
+		if ( mode==SLIST_CHR )
+			v = va_arg( ap, char * );
+		else
+			v = va_arg( ap, str * );
+
+		if ( v ) {
+			s = slist_addvp( a, mode, v );
+			if ( s==NULL ) {
+				ret = SLIST_ERR_MEMERR;
+				goto out;
+			}
+		}
+
+	} while ( v );
+
+out:
+	va_end( ap );
+	return ret;
+}
+
+int
+slist_add_all( slist *a, ... )
+{
+	int ret = SLIST_OK;
+	va_list ap;
+	str *s, *v;
+
+	va_start( ap, a );
+
+	do {
+		v = va_arg( ap, str * );
+
+		if ( v ) {
+			s = slist_addvp( a, SLIST_STR, (void*)v );
+			if ( s==NULL ) {
+				ret = SLIST_ERR_MEMERR;
+				goto out;
+			}
+		}
+
+	} while ( v );
+out:
+	va_end( ap );
+	return ret;
+}
+
+int
+slist_addc_all( slist *a, ... )
+{
+	int ret = SLIST_OK;
+	const char *v;
+	va_list ap;
+	str *s;
+
+	va_start( ap, a );
+
+	do {
+
+		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;
+			}
+		}
+
+	} while ( v );
+out:
+	va_end( ap );
+	return ret;
+}
+
+int
+slist_append( slist *a, slist *toadd )
+{
+	int i, status;
+
+	assert( a );
+	assert( toadd );
+
+	status = slist_ensure_space( a, a->n + toadd->n, SLIST_EXACT_SIZE );
+
+	if ( status == SLIST_OK ) {
+
+		for ( i=0; i<toadd->n; ++i ) {
+			str_strcpy( &(a->strs[a->n+i]), &(toadd->strs[i]) );
+			if ( str_memerr( &(a->strs[a->n+i]) ) ) return SLIST_ERR_MEMERR;
+		}
+
+		a->n += toadd->n;
+
+	}
+
+	return status;
+}
+
+int
+slist_append_unique( slist *a, slist *toadd )
+{
+	str *s;
+	int i;
+
+	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;
+	}
+
+	return SLIST_OK;
+}
+
+int
+slist_remove( slist *a, slist_index n )
+{
+	int i;
+
+	assert( a );
+
+	if ( !slist_valid_num( a, n ) ) return SLIST_ERR_BADPARAM;
+
+	for ( i=n+1; i<a->n; ++i ) {
+		str_strcpy( &(a->strs[i-1]), &(a->strs[i]) );
+		if ( str_memerr( &(a->strs[i-1]) ) ) return SLIST_ERR_MEMERR;
+	}
+
+	a->n--;
+
+	return SLIST_OK;
+}
+
+void
+slist_sort( slist *a )
+{
+	qsort( a->strs, a->n, sizeof( str ), slist_comp );
+	a->sorted = 1;
+}
+
+static slist_index
+slist_find_sorted( slist *a, const char *searchstr )
+{
+	slist_index min, max, mid;
+	str s, *cs;
+	int comp;
+
+	assert( a );
+	assert( searchstr );
+
+	str_initstrc( &s, searchstr );
+	min = 0;
+	max = a->n - 1;
+	while ( min <= max ) {
+		mid = ( min + max ) / 2;
+		cs = slist_str( a, mid );
+		comp = slist_comp( (void*)cs, (void*) (&s) );
+		if ( comp==0 ) {
+			str_free( &s );
+			return mid;
+		}
+		else if ( comp > 0 ) max = mid - 1;
+		else if ( comp < 0 ) min = mid + 1;
+	}
+	str_free( &s );
+	return -1;
+}
+
+static slist_index
+slist_find_simple( slist *a, const char *searchstr, int nocase )
+{
+	slist_index i;
+
+	assert( a );
+	assert( searchstr );
+
+	if ( nocase ) {
+		for ( i=0; i<a->n; ++i )
+			if ( !str_strcasecmpc( &(a->strs[i]), searchstr ) )
+				return i;
+	} else {
+		for ( i=0; i<a->n; ++i )
+			if ( !str_strcmpc( &(a->strs[i]), searchstr ) )
+				return i;
+	}
+	return -1;
+}
+
+slist_index
+slist_findc( slist *a, const char *searchstr )
+{
+	assert( a );
+
+	if ( a->n==0 ) return -1;
+	if ( a->sorted )
+		return slist_find_sorted( a, searchstr );
+	else
+		return slist_find_simple( a, searchstr, 0 );
+}
+
+slist_index
+slist_find( slist *a, str *searchstr )
+{
+	if ( searchstr->len==0 ) return -1;
+	return slist_findc( a, str_cstr( searchstr ) );
+}
+
+slist_index
+slist_findnocasec( slist *a, const char *searchstr )
+{
+	assert( a );
+
+	return slist_find_simple( a, searchstr, 1 );
+}
+
+slist_index
+slist_findnocase( slist *a, str *searchstr )
+{
+	if ( searchstr->len==0 ) return -1;
+	return slist_findnocasec( a, str_cstr( searchstr ) );
+}
+
+int
+slist_wasfound( slist *a, slist_index n )
+{
+	return ( n!=-1 );
+}
+
+int
+slist_wasnotfound( slist *a, slist_index n )
+{
+	return ( n==-1 );
+}
+
+int
+slist_fillfp( slist *a, FILE *fp, unsigned char skip_blank_lines )
+{
+	int ret = SLIST_OK;
+	str line, *s;
+
+	assert( a );
+	assert( fp );
+
+	slist_empty( a );
+	str_init( &line );
+
+	while ( str_fgetline( &line, fp ) ) {
+		if ( skip_blank_lines && line.len==0 ) continue;
+		s = slist_add( a, &line );
+		if ( !s ) {
+			ret = SLIST_ERR_MEMERR;
+			goto out;
+		}
+	}
+
+out:
+	str_free( &line );
+	return ret;
+}
+
+int
+slist_fill( slist *a, const char *filename, unsigned char skip_blank_lines )
+{
+	FILE *fp;
+	int ret;
+
+	fp = fopen( filename, "r" );
+	if ( !fp ) return SLIST_ERR_CANTOPEN;
+
+	ret = slist_fillfp( a, fp, skip_blank_lines );
+
+	fclose( fp );
+
+	return ret;
+}
+
+int
+slist_copy( slist *to, slist *from )
+{
+	slist_index i;
+	int status;
+
+	assert( to );
+	assert( from );
+
+	slist_free( to );
+
+	if ( from->n==0 ) return SLIST_OK;
+
+	status = slist_ensure_space( to, from->n, SLIST_EXACT_SIZE );
+
+	if ( status == SLIST_OK ) {
+
+		to->sorted = from->sorted;
+		to->n      = from->n;
+
+		for ( i=0; i<from->n; i++ ) {
+			str_strcpy( &(to->strs[i]), &(from->strs[i]) );
+			if ( str_memerr( &(to->strs[i]) ) ) return SLIST_ERR_MEMERR;
+		}
+
+	}
+	return SLIST_OK;
+}
+
+slist *
+slist_dup( slist *from )
+{
+	int status;
+	slist *to;
+
+	to = slist_new();
+	if ( to ) {
+		status = slist_copy( to, from );
+		if ( status!=SLIST_OK ) {
+			slist_delete( to );
+			to = NULL;
+		}
+	}
+
+	return to;
+}
+
+unsigned long
+slist_get_maxlen( slist *a )
+{
+	unsigned long max = 0;
+	slist_index i;
+	str *s;
+
+	assert( a );
+
+	for ( i=0; i<a->n; ++i ) {
+		s = slist_str( a, i );
+		if ( s->len > max ) max = s->len;
+	}
+
+	return max;
+}
+
+void
+slist_dump( slist *a, FILE *fp, int newline )
+{
+	slist_index i;
+
+	assert( a );
+	assert( fp );
+
+	if ( newline ) {
+		for ( i=0; i<a->n; ++i )
+			fprintf( fp, "%s\n", slist_cstr( a, i ) );
+	}
+
+	else {
+		for ( i=0; i<a->n; ++i )
+			fprintf( fp, "%s", slist_cstr( a, i ) );
+	}
+}
+
+int
+slist_match_entry( slist *a, int n, const char *s )
+{
+	assert( a );
+
+	if ( !slist_valid_num( a, n ) ) return 0;
+	if ( str_strcmpc( &(a->strs[n]), s ) ) return 0;
+	return 1;
+}
+
+void
+slist_trimend( slist *a, int n )
+{
+	slist_index i;
+
+	assert( a );
+
+	if ( a->n - n < 1 ) {
+		slist_empty( a );
+	} else {
+		for ( i=a->n -n; i<a->n; ++i ) {
+			str_empty( &(a->strs[i]) );
+		}
+		a->n -= n;
+	}
+}
+
+int
+slist_tokenizec( slist *tokens, char *p, const char *delim, int merge_delim )
+{
+	int ret = SLIST_OK;
+	str s, *t;
+	char *q;
+
+	assert( tokens );
+
+	slist_empty( tokens );
+	str_init( &s );
+	while ( p && *p ) {
+		q = p;
+		while ( *q && !strchr( delim, *q ) ) q++;
+		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; }
+		} else if ( !merge_delim ) {
+			t = slist_addvp( tokens, SLIST_CHR, (void*) "" );
+			if ( !t ) { ret = SLIST_ERR_MEMERR; goto out; }
+		}
+		p = q;
+		if ( *p ) p++;
+	}
+out:
+	str_free( &s );
+	return ret;
+}
+
+int
+slist_tokenize( slist *tokens, str *in, const char *delim, int merge_delim )
+{
+	return slist_tokenizec( tokens, str_cstr( in ), delim, merge_delim );
+}
+
+void
+slists_init( slist *a, ... )
+{
+	slist *a2;
+	va_list ap;
+	slist_init( a );
+	va_start( ap, a );
+	do {
+		a2 = va_arg( ap, slist * );
+		if ( a2 ) slist_init( a2 );
+	} while ( a2 );
+	va_end( ap );
+}
+
+void
+slists_free( slist *a, ... )
+{
+	slist *a2;
+	va_list ap;
+	slist_free( a );
+	va_start( ap, a );
+	do {
+		a2 = va_arg( ap, slist * );
+		if ( a2 ) slist_free( a2 );
+	} while ( a2 );
+	va_end( ap );
+}
+
+void
+slists_empty( slist *a, ... )
+{
+	slist *a2;
+	va_list ap;
+	slist_empty( a );
+	va_start( ap, a );
+	do {
+		a2 = va_arg( ap, slist * );
+		if ( a2 ) slist_empty( a2 );
+	} while ( a2 );
+	va_end( ap );
+}
diff --git a/bibutils/slist.h b/bibutils/slist.h
new file mode 100644
--- /dev/null
+++ b/bibutils/slist.h
@@ -0,0 +1,100 @@
+/*
+ * slist.h
+ *
+ * version: 2017-07-04
+ *
+ * Copyright (c) Chris Putnam 2004-2017
+ *
+ * Source code released under the GPL version 2
+ *
+ */
+
+#ifndef SLIST_H
+#define SLIST_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include "str.h"
+
+#define SLIST_OK            (0)
+#define SLIST_ERR_MEMERR   (-1)
+#define SLIST_ERR_CANTOPEN (-2)
+#define SLIST_ERR_BADPARAM (-3)
+
+#define SLIST_CHR (0)
+#define SLIST_STR (1)
+
+typedef int slist_index;
+
+typedef struct slist {
+	slist_index n, max;
+	int sorted;
+	str *strs;
+} slist;
+
+
+void    slists_init( slist *a, ... );
+void    slists_free( slist *a, ... );
+void    slists_empty( slist *a, ... );
+
+
+void    slist_init( slist *a );
+int     slist_init_values ( slist *a, ... );
+int     slist_init_valuesc( slist *a, ... );
+void    slist_free( slist *a );
+void    slist_empty( slist *a );
+
+slist * slist_new( void );
+void    slist_delete( slist * );
+void    slist_deletev( void *v );
+
+slist * slist_dup( slist *a );
+int     slist_copy( slist *to, slist *from );
+void    slist_swap( slist *a, slist_index n1, slist_index n2 );
+
+str *   slist_addvp( slist *a, unsigned char mode, void *vp );
+str *   slist_addc( slist *a, const char *value );
+str *   slist_add( slist *a, str *value );
+
+int     slist_addvp_all( slist *a, unsigned char mode, ... );
+int     slist_addc_all( slist *a, ... );
+int     slist_add_all( slist *a, ... );
+
+str *   slist_addvp_unique( slist *a, unsigned char mode, void *vp );
+str *   slist_addc_unique( slist *a, const char *value );
+str *   slist_add_unique( slist *a, str *value );
+
+int     slist_append( slist *a, slist *toadd );
+int     slist_append_unique( slist *a, slist *toadd );
+
+int     slist_remove( slist *a, slist_index n );
+
+str *   slist_str( slist *a, slist_index n );
+char *  slist_cstr( slist *a, slist_index n );
+
+str *   slist_set( slist *a, slist_index n, str *s );
+str *   slist_setc( slist *a, slist_index n, const char *s );
+
+void    slist_sort( slist *a );
+
+int     slist_find( slist *a, str *searchstr );
+int     slist_findc( slist *a, const char *searchstr );
+int     slist_findnocase( slist *a, str *searchstr );
+int     slist_findnocasec( slist *a, const char *searchstr );
+int     slist_wasfound( slist *a, slist_index n );
+int     slist_wasnotfound( slist *a, slist_index n );
+
+int     slist_match_entry( slist *a, slist_index n, const char *s );
+void    slist_trimend( slist *a, slist_index n );
+
+unsigned long slist_get_maxlen( slist *a );
+void    slist_dump( slist *a, FILE *fp, int newline );
+
+int     slist_fill( slist *a, const char *filename, unsigned char skip_blank_lines );
+int     slist_fillfp( slist *a, FILE *fp, unsigned char skip_blank_lines );
+int     slist_tokenize( slist *tokens, str *in, const char *delim, int merge_delim );
+int     slist_tokenizec( slist *tokens, char *p, const char *delim, int merge_delim );
+
+#endif
diff --git a/bibutils/str.c b/bibutils/str.c
new file mode 100644
--- /dev/null
+++ b/bibutils/str.c
@@ -0,0 +1,1041 @@
+/*
+ * str.c
+ *
+ * Version: 2017-07-03
+ *
+ * Copyright (c) Chris Putnam 1999-2017
+ *
+ * Source code released under the GPL version 2
+ *
+ *
+ * routines for dynamically allocated strings
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include "is_ws.h"
+#include "str.h"
+
+/* Do not use asserts in STR_NOASSERT defined */
+#ifdef STR_NOASSERT
+#define NDEBUG
+#endif
+#include <assert.h>
+
+#define str_initlen (64)
+
+
+/* Clear memory in resize/free if STR_PARANOIA defined */
+
+#ifndef STR_PARANOIA
+
+static void 
+str_realloc( str *s, unsigned long minsize )
+{
+	char *newptr;
+	unsigned long size;
+	assert( s );
+	size = 2 * s->dim;
+	if (size < minsize) size = minsize;
+	newptr = (char *) realloc( s->data, sizeof( *(s->data) )*size );
+	if ( !newptr ) {
+		fprintf(stderr,"Error.  Cannot reallocate memory (%ld bytes) in str_realloc.\n", sizeof(*(s->data))*size);
+		exit( EXIT_FAILURE );
+	}
+	s->data = newptr;
+	s->dim = size;
+}
+
+/* define as a no-op */
+#define str_nullify( s )
+
+#else
+
+static void 
+str_realloc( str *s, unsigned long minsize )
+{
+	char *newptr;
+	unsigned long size;
+	assert( s );
+	size = 2 * s->dim;
+	if ( size < minsize ) size = minsize;
+	newptr = (char *) malloc( sizeof( *(s->data) ) * size );
+	if ( !newptr ) {
+		fprintf( stderr, "Error.  Cannot reallocate memory (%d bytes)"
+			" in str_realloc.\n", sizeof(*(s->data))*size );
+		exit( EXIT_FAILURE );
+	}
+	if ( s->data ) {
+		str_nullify( s );
+		free( s->data );
+	}
+	s->data = newptr;
+	s->dim = size;
+}
+
+static inline void
+str_nullify( str *s )
+{
+	memset( s->data, 0, s->dim );
+}
+
+#endif
+
+void 
+str_init( str *s )
+{
+	assert( s );
+	s->dim = 0;
+	s->len = 0;
+	s->data = NULL;
+}
+
+void
+str_initstr( str *s, str *from )
+{
+	assert( s );
+	assert( from );
+	str_init( s );
+	str_strcpy( s, from );
+}
+
+void
+str_initstrc( str *s, const char *initstr )
+{
+	assert( s );
+	assert( initstr );
+	str_init( s );
+	str_strcpyc( s, initstr );
+}
+
+void
+str_initstrsc( str *s, ... )
+{
+	const char *c;
+	va_list ap;
+	str_init( s );
+	va_start( ap, s );
+	do {
+		c = va_arg( ap, const char * );
+		if ( c ) str_strcatc( s, c );
+	} while ( c );
+	va_end( ap );
+}
+
+void
+strs_init( str *s, ... )
+{
+	str *s2;
+	va_list ap;
+	str_init( s );
+	va_start( ap, s );
+	do {
+		s2 = va_arg( ap, str * );
+		if ( s2 ) str_init( s2 );
+	} while ( s2 );
+	va_end( ap );
+}
+
+/*
+ * This is currently a stub. Later it will
+ * report whether or not a str function
+ * could not be performed due to a memory
+ * error.
+ */
+int
+str_memerr( str *s )
+{
+	return 0;
+}
+
+void
+str_mergestrs( str *s, ... )
+{
+	va_list ap;
+	const char *cp;
+	str_empty( s );
+	va_start( ap, s );
+	do {
+		cp = va_arg( ap, const char * );
+		if ( cp ) str_strcatc( s, cp );
+	} while ( cp );
+	va_end( ap );
+}
+
+static void 
+str_initalloc( str *s, unsigned long minsize )
+{
+	unsigned long size = str_initlen;
+	assert( s );
+	if ( minsize > str_initlen ) size = minsize;
+	s->data = (char *) malloc (sizeof( *(s->data) ) * size);
+	if ( !s->data ) {
+		fprintf(stderr,"Error.  Cannot allocate memory in str_initalloc.\n");
+		exit( EXIT_FAILURE );
+	}
+	s->data[0]='\0';
+	s->dim=size;
+	s->len=0;
+}
+
+str *
+str_new( void )
+{
+	str *s = (str *) malloc( sizeof( *s ) );
+	if ( s )
+		str_initalloc( s, str_initlen );
+	return s;
+}
+
+void 
+str_free( str *s )
+{
+	assert( s );
+	if ( s->data ) {
+		str_nullify( s );
+		free( s->data );
+	}
+	s->dim = 0;
+	s->len = 0;
+	s->data = NULL;
+}
+
+void
+strs_free( str *s, ... )
+{
+	str *s2;
+	va_list ap;
+	str_free( s );
+	va_start( ap, s );
+	do {
+		s2 = va_arg( ap, str * );
+		if ( s2 ) str_free( s2 );
+	} while ( s2 );
+	va_end( ap );
+}
+
+void
+str_delete( str *s )
+{
+	assert( s );
+	str_free( s );
+	free( s );
+}
+
+void
+str_empty( str *s )
+{
+	assert( s );
+	if ( s->data ) {
+		str_nullify( s );
+		s->data[0] = '\0';
+	}
+	s->len = 0;
+}
+
+void
+strs_empty( str *s, ... )
+{
+	str *s2;
+	va_list ap;
+	str_empty( s );
+	va_start( ap, s );
+	do {
+		s2 = va_arg( ap, str * );
+		if ( s2 ) str_empty( s2 );
+	} while ( s2 );
+	va_end( ap );
+}
+
+void
+str_addchar( str *s, char newchar )
+{
+	assert( s );
+	if ( newchar=='\0' ) return; /* appending '\0' is a null operation */
+	if ( !s->data || s->dim==0 ) 
+		str_initalloc( s, str_initlen );
+	if ( s->len + 2 > s->dim ) 
+		str_realloc( s, s->len*2 );
+	s->data[s->len++] = newchar;
+	s->data[s->len] = '\0';
+}
+
+/* str_addutf8
+ *
+ * Add potential multibyte character to s starting at pointer p.
+ * Multibyte Unicode characters have the high bit set.
+ *
+ * Since we can progress more than one byte at p, return the
+ * properly updated pointer p.
+ */
+const char *
+str_addutf8( str *s, const char *p )
+{
+	if ( ! ((*p) & 128 ) ) {
+		str_addchar( s, *p );
+		p++;
+	} else {
+		while ( ((*p) & 128) ) {
+			str_addchar( s, *p );
+			p++;
+		}
+	}
+	return p;
+}
+
+char *
+str_cstr( str *s )
+{
+	assert( s );
+	return s->data;
+}
+
+void 
+str_fprintf( FILE *fp, str *s )
+{
+	assert( s );
+	if ( s->data ) fprintf( fp, "%s", s->data );
+}
+
+void
+str_prepend( str *s, const char *addstr )
+{
+	unsigned long lenaddstr, i;
+	assert( s && addstr );
+	lenaddstr = strlen( addstr );
+	if ( lenaddstr==0 ) return;
+	if ( !s->data || !s->dim )
+		str_initalloc( s, lenaddstr+1 );
+	else {
+		if ( s->len + lenaddstr  + 1 > s->dim )
+			str_realloc( s, s->len + lenaddstr + 1 );
+		for ( i=s->len+lenaddstr-1; i>=lenaddstr; i-- )
+			s->data[i] = s->data[i-lenaddstr];
+	}
+	strncpy( s->data, addstr, lenaddstr );
+	s->len += lenaddstr;
+	s->data[ s->len ] = '\0';
+}
+
+static inline void
+str_strcat_ensurespace( str *s, unsigned long n )
+{
+	unsigned long m = s->len + n + 1;
+	if ( !s->data || !s->dim )
+		str_initalloc( s, m );
+	else if ( s->len + n + 1 > s->dim )
+		str_realloc( s, m );
+}
+
+static inline void 
+str_strcat_internal( str *s, const char *addstr, unsigned long n )
+{
+	str_strcat_ensurespace( s, n );
+	strncat( &(s->data[s->len]), addstr, n );
+	s->len += n;
+	s->data[s->len]='\0';
+}
+
+void
+str_strcat( str *s, str *from )
+{
+	assert ( s && from );
+	if ( !from->data ) return;
+	else str_strcat_internal( s, from->data, from->len );
+}
+
+void
+str_strcatc( str *s, const char *from )
+{
+	unsigned long n;
+	assert( s && from );
+	n = strlen( from );
+	str_strcat_internal( s, from, n );
+}
+
+void
+str_segcat( str *s, char *startat, char *endat )
+{
+	unsigned long n;
+	char *p;
+
+	assert( s && startat && endat );
+	assert( (size_t) startat < (size_t) endat );
+
+	if ( startat==endat ) return;
+
+	n = 0;
+	p = startat;
+	while ( p!=endat ) {
+		n++;
+		p++;
+	}
+
+	str_strcat_internal( s, startat, n );
+}
+
+void
+str_indxcat( str *s, char *p, unsigned long start, unsigned long stop )
+{
+	unsigned long i;
+	assert( s && p );
+	assert( start <= stop );
+	for ( i=start; i<stop; ++i )
+		str_addchar( s, p[i] );
+}
+
+/* str_cpytodelim()
+ *     term      = string of characters to be used as terminators
+ *     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 )
+{
+	assert( s );
+	str_empty( s );
+	return str_cattodelim( s, p, delim, finalstep );
+}
+
+/* str_cpytodelim()
+ *     term      = string of characters to be used as terminators
+ *     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 )
+{
+	assert( s );
+	while ( p && *p && !strchr( delim, *p ) ) {
+		str_addchar( s, *p );
+		p++;
+	}
+	if ( p && *p && finalstep ) p++;
+	return p;
+}
+
+static inline void
+str_strcpy_ensurespace( str *s, unsigned long n )
+{
+	unsigned long m = n + 1;
+	if ( !s->data || !s->dim )
+		str_initalloc( s, m );
+	else if ( m > s->dim )
+		str_realloc( s, m );
+}
+
+static inline void
+str_strcpy_internal( str *s, const char *p, unsigned long n )
+{
+	str_strcpy_ensurespace( s, n );
+	strncpy( s->data, p, n );
+	s->data[n] = '\0';
+	s->len = n;
+}
+
+void
+str_strcpy( str *s, str *from )
+{
+	assert( s );
+	assert( from );
+	if ( s==from ) return;
+	else if ( !from || from->len==0 ) str_empty( s );
+	else str_strcpy_internal( s, from->data, from->len );
+}
+
+void 
+str_strcpyc( str *s, const char *from )
+{
+	unsigned long n;
+	assert( s && from );
+	n = strlen( from );
+	str_strcpy_internal( s, from, n );
+}
+
+/* str_segcpy( s, start, end );
+ *
+ * copies [start,end) into s
+ */
+void
+str_segcpy( str *s, char *startat, char *endat )
+{
+	unsigned long n;
+	char *p;
+
+	assert( s && startat && endat );
+	assert( ((size_t) startat) <= ((size_t) endat) );
+
+	if ( startat==endat ) {
+		str_empty( s );
+		return;
+	}
+
+	n = 0;
+	p = startat;
+	while ( p!=endat ) {
+		p++;
+		n++;
+	}
+
+	str_strcpy_internal( s, startat, n );
+}
+
+/*
+ * str_indxcpy( s, in, start, stop );
+ *
+ * copies in[start,stop) (excludes stop) into s
+ */
+void
+str_indxcpy( str *s, char *p, unsigned long start, unsigned long stop )
+{
+	unsigned long i;
+	assert( s && p );
+	assert( start <= stop );
+	if ( start == stop ) {
+		str_empty( s );
+		return;
+	}
+	str_strcpy_ensurespace( s, stop-start+1 );
+	for ( i=start; i<stop; ++i )
+		s->data[i-start] = p[i];
+	s->len = stop-start;
+	s->data[s->len] = '\0';
+}
+
+str *
+str_strdup( str *from )
+{
+	str *s = str_new();
+	if ( s )
+		str_strcpy( s, from );
+	return s;
+}
+
+str *
+str_strdupc( const char *from )
+{
+	str *s = str_new();
+	if ( s )
+		str_strcpyc( s, from );
+	return s;
+}
+
+void
+str_segdel( str *s, char *p, char *q )
+{
+	str tmp1, tmp2;
+	char *r;
+	assert( s );
+	r = &(s->data[s->len]);
+	str_init( &tmp1 );
+	str_init( &tmp2 );
+	str_segcpy( &tmp1, s->data, p );
+	str_segcpy( &tmp2, q, r );
+	str_empty( s );
+	if ( tmp1.data ) str_strcat( s, &tmp1 );
+	if ( tmp2.data ) str_strcat( s, &tmp2 );
+	str_free( &tmp2 );
+	str_free( &tmp1 );
+}
+
+/*
+ * str_findreplace()
+ *
+ *   if replace is "" or NULL, then delete find
+ */
+
+int
+str_findreplace( str *s, const char *find, const char *replace )
+{
+	long diff;
+	size_t findstart, searchstart;
+	size_t p1, p2;
+	size_t find_len, rep_len, curr_len;
+	char empty[2] = "";
+	unsigned long minsize;
+	char *p;
+	int n = 0;
+
+	assert( s && find );
+	if ( !s->data || !s->dim ) return n;
+	if ( !replace ) replace = empty;
+
+	find_len = strlen( find );
+	rep_len  = strlen( replace );
+	diff     = rep_len - find_len;
+	if ( diff < 0 ) diff = 0;
+
+	searchstart=0;
+	while ((p=strstr(s->data + searchstart,find))!=NULL) {
+		curr_len = strlen(s->data);
+		findstart=(size_t) p - (size_t) s->data;
+		minsize = curr_len + diff + 1;
+		if (s->dim <= minsize) str_realloc( s, minsize );
+		if ( find_len > rep_len ) {
+			p1 = findstart + rep_len;
+			p2 = findstart + find_len;
+			while( s->data[p2] )
+				s->data[p1++]=s->data[p2++];
+			s->data[p1]='\0';
+			n++;
+		} else if ( find_len < rep_len ) {
+			for ( p1=curr_len; p1>=findstart+find_len; p1-- )
+				s->data[p1+diff] = s->data[p1];
+			n++;
+		}
+		for (p1=0; p1<rep_len; p1++)
+			s->data[findstart+p1]=replace[p1];
+		searchstart = findstart + rep_len; 
+		s->len += rep_len - find_len;
+	}
+	return n;
+}
+
+
+/* str_fget()
+ *   returns 0 if we're done, 1 if we're not done
+ *   extracts line by line (regardless of end characters)
+ *   and feeds from buf....
+ */
+int
+str_fget( FILE *fp, char *buf, int bufsize, int *pbufpos, str *outs )
+{
+	int  bufpos = *pbufpos, done = 0;
+	char *ok;
+	assert( fp && outs );
+	str_empty( outs );
+	while ( !done ) {
+		while ( buf[bufpos] && buf[bufpos]!='\r' && buf[bufpos]!='\n' )
+			str_addchar( outs, buf[bufpos++] );
+		if ( buf[bufpos]=='\0' ) {
+			ok = fgets( buf, bufsize, fp );
+			bufpos=*pbufpos=0;
+			if ( !ok && feof(fp) ) { /* end-of-file */
+				buf[bufpos] = 0;
+				if ( outs->len==0 ) return 0; /*nothing in out*/
+				else return 1; /*one last out */
+			}
+		} else if ( buf[bufpos]=='\r' || buf[bufpos]=='\n' ) done=1;
+	}
+	if ( ( buf[bufpos]=='\n' && buf[bufpos+1]=='\r') ||
+	     ( buf[bufpos]=='\r' && buf[bufpos+1]=='\n') ) bufpos+=2;
+	else if ( buf[bufpos]=='\n' || buf[bufpos]=='\r' ) bufpos+=1; 
+	*pbufpos = bufpos;
+	return 1;
+}
+
+void
+str_toupper( str *s )
+{
+	unsigned long i;
+	assert( s );
+	for ( i=0; i<s->len; ++i )
+		s->data[i] = toupper( (unsigned char)s->data[i] );
+}
+
+void
+str_tolower( str *s )
+{
+	unsigned long i;
+	assert( s );
+	for ( i=0; i<s->len; ++i )
+		s->data[i] = tolower( (unsigned char)s->data[i] );
+}
+
+/* str_swapstrings( s1, s2 )
+ * be sneaky and swap internal string data from one
+ * string to another
+ */
+void
+str_swapstrings( str *s1, str *s2 )
+{
+	char *tmpp;
+	int tmp;
+
+	assert( s1 && s2 );
+
+	/* swap dimensioning info */
+	tmp = s1->dim;
+	s1->dim = s2->dim;
+	s2->dim = tmp;
+
+	/* swap length info */
+	tmp = s1->len;
+	s1->len = s2->len;
+	s2->len = tmp;
+
+	/* swap data */
+	tmpp = s1->data;
+	s1->data = s2->data;
+	s2->data = tmpp;
+}
+
+void
+str_trimstartingws( str *s )
+{
+	char *p, *q;
+	int n;
+
+	assert( s );
+
+	if ( s->len==0 || !is_ws( s->data[0] ) ) return;
+
+	n = 0;
+	p = s->data;
+	while ( is_ws( *p ) ) p++;
+
+	q = s->data;
+	while ( *p ) {
+		*q++ = *p++;
+		n++;
+	}
+	*q = '\0';
+
+	s->len = n;
+}
+
+void
+str_trimendingws( str *s )
+{
+	assert( s );
+	while ( s->len > 0 && is_ws( s->data[s->len-1] ) ) {
+		s->data[s->len-1] = '\0';
+		s->len--;
+	}
+}
+
+int
+str_match_first( str *s, char ch )
+{
+	assert( s );
+	if ( !s->len ) return 0;
+	if ( s->data[0] == ch ) return 1;
+	return 0;
+}
+
+int
+str_match_end( str *s, char ch )
+{
+	assert( s );
+	if ( !s->len ) return 0;
+	if ( s->data[ s->len - 1 ] == ch ) return 1;
+	return 0;
+}
+
+void
+str_trimbegin( str *s, unsigned long n )
+{
+	char *p, *q;
+
+	assert( s );
+
+	if ( n==0 ) return;
+	if ( s->len==0 ) return;
+	if ( n >= s->len ) {
+		str_empty( s );
+		return;
+	}
+
+	p = s->data;
+	while ( n-- > 0 ) p++;
+
+	n = 0;
+	q = s->data;
+	while ( *p ) {
+		*q++ = *p++;
+		n++;
+	}
+	*q = '\0';
+
+	s->len = n;
+}
+
+void
+str_trimend( str *s, unsigned long n )
+{
+	assert( s );
+
+	if ( n==0 ) return;
+	if ( n >= s->len ) {
+		str_empty( s );
+		return;
+	}
+
+	s->len -= n;
+	s->data[ s->len ] = '\0';
+}
+
+void
+str_pad( str *s, unsigned long len, char ch )
+{
+	unsigned long i;
+	assert( s );
+	for ( i=s->len; i<len; i++ )
+		str_addchar( s, ch );
+}
+
+void
+str_copyposlen( str *s, str *in, unsigned long pos, unsigned long len )
+{
+	unsigned long i, max;
+	assert( s );
+	str_empty( s );
+	max = pos+len;
+	if ( max > in->len ) max = in->len;
+	for ( i=pos; i<max; ++i )
+		str_addchar( s, in->data[i] );
+}
+
+static void
+str_check_case( str *s, int *lowercase, int *uppercase )
+{
+	int i;
+	assert( s );
+	*lowercase = 0;
+	*uppercase = 0;
+	if ( s->len < 1 ) return;
+	for ( i=0; i<s->len && !( *lowercase && *uppercase ); ++i ) {
+		if ( isalpha( (unsigned char)s->data[i] ) ) {
+			if ( isupper( (unsigned char)s->data[i] ) ) *uppercase += 1;
+			else if ( islower( (unsigned char)s->data[i] ) ) *lowercase += 1;
+		}
+	}
+}
+
+int
+str_is_mixedcase( str *s )
+{
+	int lowercase, uppercase;
+	str_check_case( s, &lowercase, &uppercase );
+	if ( lowercase > 0 && uppercase > 0 ) return 1;
+	return 0;
+}
+
+int
+str_is_lowercase( str *s )
+{
+	int lowercase, uppercase;
+	str_check_case( s, &lowercase, &uppercase );
+	if ( lowercase > 0 && uppercase == 0 ) return 1;
+	return 0;
+}
+
+int
+str_is_uppercase( str *s )
+{
+	int lowercase, uppercase;
+	str_check_case( s, &lowercase, &uppercase );
+	if ( lowercase == 0 && uppercase > 0 ) return 1;
+	return 0;
+}
+
+void
+str_stripws( str *s )
+{
+	unsigned long len = 0;
+	char *p, *q;
+	assert( s );
+	if ( s->len ) {
+		p = q = s->data;
+		while ( *p ) {
+			if ( !is_ws( *p ) ) {
+				*q = *p;
+				q++;
+				len++;
+			}
+			p++;
+		}
+		*q = '\0';
+	}
+	s->len = len;
+}
+
+int
+str_strcmp( const str *s, const str *t )
+{
+	assert( s );
+	assert( t );
+	if ( s->len == 0 && t->len == 0 ) return 0;
+	if ( s->len == 0 ) return strcmp( "", t->data );
+	if ( t->len == 0 ) return strcmp( s->data, "" );
+	return strcmp( s->data, t->data );
+}
+
+int
+str_strcmpc( const str *s, const char *t )
+{
+	assert( s );
+	assert( t );
+	if ( s->len == 0 ) return strcmp( "", t );
+	return strcmp( s->data, t );
+}
+
+int
+str_strncmp( const str *s, const str *t, size_t n )
+{
+	assert( s );
+	assert( t );
+	if ( s->len == 0 && t->len == 0 ) return 0;
+	if ( s->len == 0 ) return strncmp( "", t->data, n );
+	if ( t->len == 0 ) return strncmp( s->data, "", n );
+	return strncmp( s->data, t->data, n );
+}
+
+int
+str_strncmpc( const str *s, const char *t, size_t n )
+{
+	assert( s );
+	assert( t );
+	if ( s->len == 0 ) return strncmp( "", t, n );
+	return strncmp( s->data, t, n );
+}
+
+int
+str_strcasecmp( const str *s, const str *t )
+{
+	assert( s );
+	assert( t );
+	if ( s->len == 0 && t->len == 0 ) return 0;
+	if ( s->len == 0 ) return strcasecmp( "", t->data );
+	if ( t->len == 0 ) return strcasecmp( s->data, "" );
+	return strcasecmp( s->data, t->data );
+}
+
+int
+str_strcasecmpc( const str *s, const char *t )
+{
+	assert( s );
+	assert( t );
+	if ( s->len == 0 ) return strcasecmp( "", t );
+	return strcasecmp( s->data, t );
+}
+
+char *
+str_strstr( const str *s, const str *t )
+{
+	assert( s );
+	assert( t );
+	if ( s->len == 0 && t->len == 0 ) return strstr( "", "" );
+	if ( s->len == 0 ) return strstr( "", t->data );
+	if ( t->len == 0 ) return strstr( s->data, "" );
+	return strstr( s->data, t->data );
+}
+
+char *
+str_strstrc( const str *s, const char *t )
+{
+	assert( s );
+	assert( t );
+	if ( s->len == 0 ) return strstr( "", t );
+	return strstr( s->data, t );
+}
+
+void
+str_reverse( str *s )
+{
+	unsigned long i, max;
+	char tmp;
+	assert( s );
+	max = s->len / 2;
+	for ( i=0; i<max; ++i ) {
+		tmp = s->data[ i ];
+		s->data[ i ] = s->data[ s->len - 1 - i ];
+		s->data[ s->len - 1 - i ] = tmp;
+	}
+}
+
+int
+str_fgetline( str *s, FILE *fp )
+{
+	int ch, eol = 0;
+	assert( s );
+	assert( fp );
+	str_empty( s );
+	if ( feof( fp ) ) return 0;
+	while ( !feof( fp ) && !eol ) {
+		ch = fgetc( fp );
+		if ( ch == EOF ) {
+			if ( s->len ) return 1;
+			else return 0;
+		}
+		else if ( ch == '\n' ) eol = 1;
+		else if ( ch == '\r' ) {
+			ch = fgetc( fp );
+			if ( ch != '\n' ) ungetc( ch, fp );
+			eol = 1;
+		} else {
+			str_addchar( s, (char) ch );
+		}
+	}
+	return 1;
+}
+
+/*
+ * s = "Hi!\0", s.len = 3
+ *
+ * str_char( s, 0 ) = 'H'  str_revchar( s, 0 ) = '!'
+ * str_char( s, 1 ) = 'i'  str_revchar( s, 1 ) = 'i'
+ * str_char( s, 2 ) = '!'  str_revchar( s, 2 ) = 'H'
+ * str_char( s, 3 ) = '\0' str_revchar( s, 3 ) = '\0'
+ */
+char
+str_char( str *s, unsigned long n )
+{
+	assert( s );
+	if ( s->len==0 || n >= s->len ) return '\0';
+	return s->data[ n ];
+}
+
+char
+str_revchar( str *s, unsigned long n )
+{
+	assert( s );
+	if ( s->len==0 || n >= s->len ) return '\0';
+	return s->data[ s->len - n - 1];
+}
+
+void
+str_makepath( str *path, const char *dirname, const char *filename, char sep )
+{
+	assert( path );
+	if ( dirname ) str_strcpyc( path, dirname );
+	else str_empty( path );
+
+	if ( path->len && path->data[path->len-1]!=sep )
+		str_addchar( path, sep );
+
+	if ( filename ) str_strcatc( path, filename );
+}
+
+void
+str_fill( str *s, unsigned long n, char fillchar )
+{
+	unsigned long i;
+	assert( s );
+	if ( !s->data || s->dim==0 )
+		str_initalloc( s, n+1 );
+	if ( n + 1 > s->dim )
+		str_realloc( s, n+1 );
+	for ( i=0; i<n; ++i )
+		s->data[i] = fillchar;
+	s->data[n] = '\0';
+	s->len = n;
+}
+
+int
+str_has_value( str *s )
+{
+	if ( !s || s->len==0 ) return 0;
+	return 1;
+}
+
+int
+str_is_empty( str *s )
+{
+	if ( !s || s->len==0 ) return 1;
+	return 0;
+}
diff --git a/bibutils/str.h b/bibutils/str.h
new file mode 100644
--- /dev/null
+++ b/bibutils/str.h
@@ -0,0 +1,124 @@
+/*
+ * str.h
+ *
+ * Version: 2017-07-03
+ *
+ * Copyright (c) Chris Putnam 1999-2017
+ *
+ * Source code released under the GPL version 2
+ *
+ */
+#ifndef STR_H
+#define STR_H
+
+#include <stdio.h>
+
+typedef struct str {
+	char *data;
+	unsigned long dim;
+	unsigned long len;
+}  str;
+
+str *  str_new         ( void );
+void   str_delete      ( str *s );
+
+void   str_init        ( str *s );
+void   str_initstr     ( str *s, str *from );
+void   str_initstrc    ( str *s, const char *initstr );
+void   str_initstrsc   ( str *s, ... );
+void   str_empty       ( str *s );
+void   str_free        ( str *s );
+
+void   strs_init       ( str *s, ... );
+void   strs_empty      ( str *s, ... );
+void   strs_free       ( str *s, ... );
+
+str*   str_strdup ( str *s );
+str*   str_strdupc( const char *p );
+
+void   str_strcat ( str *s, str *from );
+void   str_strcatc( str *s, const char *from );
+
+void   str_strcpy ( str *s, str *from );
+void   str_strcpyc( str *s, const char *from );
+
+int    str_strcmp ( const str *s, const str *t );
+int    str_strcmpc( const str *s, const char *t );
+
+int    str_strncmp ( const str *s, const str *t,  size_t n );
+int    str_strncmpc( const str *s, const char *t, size_t n );
+
+int    str_strcasecmp ( const str *s, const str *t );
+int    str_strcasecmpc( const str *s, const char *t );
+
+char * str_strstr ( const str *s, const str *t );
+char * str_strstrc( const str *s, const char *t );
+
+void str_prepend     ( str *s, const char *addstr );
+void str_mergestrs   ( str *s, ... );
+
+void str_addchar     ( str *s, char newchar );
+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 );
+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 );
+void str_indxcpy     ( str *s, char *p, unsigned long start, unsigned long stop );
+void str_indxcat     ( str *s, char *p, unsigned long start, unsigned long stop );
+void str_fprintf     ( FILE *fp, str *s );
+int  str_fget        ( FILE *fp, char *buf, int bufsize, int *pbufpos,
+                          str *outs );
+char * str_cstr      ( str *s );
+char str_char        ( str *s, unsigned long n );
+char str_revchar     ( str *s, unsigned long n );
+int  str_fgetline    ( str *s, FILE *fp );
+int  str_findreplace ( str *s, const char *find, const char *replace );
+void str_toupper     ( str *s );
+void str_tolower     ( str *s );
+void str_trimstartingws( str *s );
+void str_trimendingws( str *s );
+void str_swapstrings ( str *s1, str *s2 );
+void str_stripws     ( str *s );
+
+int  str_match_first ( str *s, char ch );
+int  str_match_end   ( str *s, char ch );
+void str_trimbegin   ( str *s, unsigned long n );
+void str_trimend     ( str *s, unsigned long n );
+
+void str_pad         ( str *s, unsigned long len, char ch );
+void str_copyposlen  ( str *s, str *in, unsigned long pos, unsigned long len );
+
+void str_makepath    ( str *path, const char *dirname, const char *filename, char sep );
+
+void str_fill        ( str *s, unsigned long n, char fillchar );
+
+int  str_is_mixedcase( str *s );
+int  str_is_lowercase( str *s );
+int  str_is_uppercase( str *s );
+
+int  str_memerr( str *s );
+
+int  str_has_value( str *s );
+int  str_is_empty( str *s );
+
+
+/* #define STR_PARANOIA
+ *
+ * set to clear memory before it is freed or reallocated
+ * note that this is slower...may be important if string
+ * contains sensitive information
+ */
+
+/* #define STR_NOASSERT
+ *
+ * set to turn off the use of asserts (and associated call to exit)
+ * in str functions...useful for library construction for
+ * Linux distributions that don't want libraries calling exit, but
+ * not useful during code development
+ */
+
+#endif
+
diff --git a/bibutils/str_conv.c b/bibutils/str_conv.c
new file mode 100644
--- /dev/null
+++ b/bibutils/str_conv.c
@@ -0,0 +1,196 @@
+/*
+ * str_conv.c
+ *
+ * Copyright (c) Chris Putnam 1999-2017
+ *
+ * Source code released under the GPL version 2
+ *
+ * str routines for converting strs between character sets
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include "latex.h"
+#include "entities.h"
+#include "utf8.h"
+#include "gb18030.h"
+#include "charsets.h"
+#include "str_conv.h"
+
+static void
+addentity( str *s, unsigned int ch )
+{
+	char buf[512];
+	sprintf( buf, "&#%d;", ch );
+	str_strcatc( s, buf );
+}
+
+/* These are the five minimal predefined entites in XML */
+static int
+minimalxmlchars( str *s, unsigned int ch )
+{
+	if ( ch==34 )      { str_strcatc( s, "&quot;" ); return 1; }
+	else if ( ch==38 ) { str_strcatc( s, "&amp;" );  return 1; }
+	else if ( ch==39 ) { str_strcatc( s, "&apos;" ); return 1; }
+	else if ( ch==60 ) { str_strcatc( s, "&lt;" );   return 1; }
+	else if ( ch==62 ) { str_strcatc( s, "&gt;" );   return 1; }
+	return 0;
+}
+
+static void
+addxmlchar( str *s, unsigned int ch )
+{
+	if ( minimalxmlchars( s, ch ) ) return;
+	if ( ch > 127 ) addentity( s, ch );
+	else str_addchar( s, ch );
+}
+
+static void
+addutf8char( str *s, unsigned int ch, int xmlout )
+{
+	unsigned char code[6];
+	int nc, i;
+	if ( xmlout ) {
+		if ( minimalxmlchars( s, ch ) ) return;
+		if ( ch > 127 && xmlout == STR_CONV_XMLOUT_ENTITIES )
+			{ addentity( s, ch ); return; }
+	}
+	nc = utf8_encode( ch, code );
+	for ( i=0; i<nc; ++i )
+		str_addchar( s, code[i] );
+}
+
+static void
+addgb18030char( str *s, unsigned int ch, int xmlout )
+{
+	unsigned char code[4];
+	int nc, i;
+	if ( xmlout ) {
+		if ( minimalxmlchars( s, ch ) ) return;
+		if ( ch > 127 && xmlout == STR_CONV_XMLOUT_ENTITIES )
+			{ addentity( s, ch ); return; }
+	}
+	nc = gb18030_encode( ch, code );
+	for ( i=0; i<nc; ++i )
+		str_addchar( s, code[i] );
+}
+
+static void
+addlatexchar( str *s, unsigned int ch, int xmlout, int utf8out )
+{
+	char buf[512];
+	uni2latex( ch, buf, sizeof( buf ) );
+	/* If the unicode character isn't recognized as latex output
+	 * a '?' unless the user has requested unicode output.  If so,
+	 * output the unicode.
+	 */
+	if ( utf8out && !strcmp( buf, "?" ) ) {
+		addutf8char( s, ch, xmlout );
+	} else {
+		str_strcatc( s, buf );
+	}
+}
+
+/*
+ * get_unicode()
+ * 
+ *   This can be a little tricky.  If the character is simply encoded
+ *   such as UTF8 for > 128 or by numeric xml entities such as "&#534;"
+ *   then the output of decode_entity() and utf8_decode will necessarily
+ *   be in the charsetin character set.  On the other hand, if it's a
+ *   fancy latex expression, such as "\alpha", or a non-numeric xml entity
+ *   like "&amp;", then we'll get the Unicode value (because our lists only
+ *   keep the Unicode equivalent).
+ *
+ *   The unicode variable indicates whether or not a Unicode-based listing
+ *   was used to convert the character (remember that charsetin could be
+ *   Unicode independently).
+ *
+ *   The charset variable is used to keep track of what character set
+ *   the character is in prior to conversion.
+ *
+ */
+
+static unsigned int
+get_unicode( str *s, unsigned int *pi, int charsetin, int latexin, int utf8in, int xmlin )
+{
+	unsigned int ch;
+	int unicode = 0, err = 0;
+	if ( xmlin && s->data[*pi]=='&' ) {
+		ch = decode_entity( s->data, pi, &unicode, &err );
+	} else if ( charsetin==CHARSET_GB18030 ) {
+		ch = gb18030_decode( s->data, pi );
+		unicode = 1;
+	} else if ( latexin ) {
+		/* Must handle bibtex files in UTF8/Unicode */
+		if ( utf8in && ( s->data[*pi] & 128 ) ) {
+			ch = utf8_decode( s->data, pi );
+			unicode = 1;
+		} else ch = latex2char( s->data, pi, &unicode );
+	}
+	else if ( utf8in )
+		ch = utf8_decode( s->data, pi );
+	else {
+		ch = (unsigned int) s->data[*pi];
+		*pi = *pi + 1;
+	}
+	if ( !unicode && charsetin!=CHARSET_UNICODE )
+		ch = charset_lookupchar( charsetin, ch );
+	return ch;
+}
+
+static int
+write_unicode( str *s, unsigned int ch, int charsetout, int latexout,
+		int utf8out, int xmlout )
+{
+	unsigned int c;
+	if ( latexout ) {
+		addlatexchar( s, ch, xmlout, utf8out );
+	} else if ( utf8out ) {
+		addutf8char( s, ch, xmlout );
+	} else if ( charsetout==CHARSET_GB18030 ) {
+		addgb18030char( s, ch, xmlout );
+	} else {
+		c = charset_lookupuni( charsetout, ch );
+		if ( xmlout ) addxmlchar( s, c );
+		else str_addchar( s, c );
+	}
+	return 1;
+}
+
+/*
+ * Returns 1 on memory error condition
+ */
+int
+str_convert( str *s,
+	int charsetin,  int latexin,  int utf8in,  int xmlin,
+	int charsetout, int latexout, int utf8out, int xmlout )
+{
+	unsigned int pos = 0;
+	unsigned int ch;
+	str ns;
+	int ok = 1;
+
+	if ( !s || s->len==0 ) return ok;
+
+	str_init( &ns );
+
+	if ( charsetin==CHARSET_UNKNOWN ) charsetin = CHARSET_DEFAULT;
+	if ( charsetout==CHARSET_UNKNOWN ) charsetout = CHARSET_DEFAULT;
+
+	while ( s->data[pos] ) {
+		ch = get_unicode( s, &pos, charsetin, latexin, utf8in, xmlin );
+		ok = write_unicode( &ns, ch, charsetout, latexout, utf8out, xmlout );
+		if ( !ok ) goto out;
+	}
+
+	str_swapstrings( s, &ns );
+out:
+	str_free( &ns );
+
+	return ok;
+}
+
diff --git a/bibutils/str_conv.h b/bibutils/str_conv.h
new file mode 100644
--- /dev/null
+++ b/bibutils/str_conv.h
@@ -0,0 +1,23 @@
+/*
+ * str_conv.h
+ *
+ * Copyright (c) Chris Putnam 1999-2017
+ *
+ * Source code released under the GPL version 2
+ *
+ */
+#ifndef STR_CONV_H
+#define STR_CONV_H
+
+#define STR_CONV_XMLOUT_FALSE    (0)
+#define STR_CONV_XMLOUT_TRUE     (1)
+#define STR_CONV_XMLOUT_ENTITIES (3)
+
+#include "str.h"
+
+extern int str_convert( str *s,
+		int charsetin, int latexin, int utf8in, int xmlin, 
+		int charsetout, int latexout, int utf8out, int xmlout );
+
+#endif
+
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-2013
+ * Copyright (c) Chris Putnam 1995-2017
  *
  * 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-2013
+ * Copyright (c) Chris Putnam 1995-2017
  *
  * 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-2014
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
@@ -11,7 +11,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "newstr.h"
+#include "str.h"
 #include "fields.h"
 #include "title.h"
 #include "is_ws.h"
@@ -20,12 +20,12 @@
 title_process( fields *info, char *tag, char *data, int level, 
 	unsigned char nosplittitle )
 {
-	newstr title, subtitle;
+	str title, subtitle;
 	char *p, *q;
 	int status;
 
-	newstr_init( &title );
-	newstr_init( &subtitle );
+	str_init( &title );
+	str_init( &subtitle );
 
 	if ( nosplittitle ) q = NULL;
 	else {
@@ -33,36 +33,68 @@
 		if ( !q ) q = strstr( data, "? " );
 	}
 
-	if ( !q ) newstr_strcpy( &title, data );
+	if ( !q ) str_strcpyc( &title, data );
 	else {
 		p = data;
-		while ( p!=q ) newstr_addchar( &title, *p++ );
-		if ( *q=='?' ) newstr_addchar( &title, '?' );
+		while ( p!=q ) str_addchar( &title, *p++ );
+		if ( *q=='?' ) str_addchar( &title, '?' );
 		q++;
 		q = skip_ws( q );
-		while ( *q ) newstr_addchar( &subtitle, *q++ );
+		while ( *q ) str_addchar( &subtitle, *q++ );
 	}
 
 	if ( strncasecmp( "SHORT", tag, 5 ) ) {
-		if ( title.len>0 ) {
-			status = fields_add( info, "TITLE", title.data, level );
+		if ( str_has_value( &title ) ) {
+			status = fields_add( info, "TITLE", str_cstr( &title ), level );
 			if ( status!=FIELDS_OK ) return 0;
 		}
-		if ( subtitle.len>0 ) {
-			status = fields_add( info, "SUBTITLE", subtitle.data, level );
+		if ( str_has_value( &subtitle ) ) {
+			status = fields_add( info, "SUBTITLE", str_cstr( &subtitle ), level );
 			if ( status!=FIELDS_OK ) return 0;
 		}
 	} else {
-		if ( title.len>0 ) {
-			status = fields_add( info, "SHORTTITLE", title.data, level );
+		if ( str_has_value( &title ) ) {
+			status = fields_add( info, "SHORTTITLE", str_cstr( &title ), level );
 			if ( status!=FIELDS_OK ) return 0;
 		}
 		/* no SHORT-SUBTITLE! */
 	}
 
-	newstr_free( &subtitle );
-	newstr_free( &title );
+	str_free( &subtitle );
+	str_free( &title );
 
 	return 1;
 }
 
+/* title_combine()
+ *
+ * Combine a main title and a subtitle into a full title.
+ *
+ * Example:
+ * 	Main title = "A Clearing in the Distance"
+ * 	Subtitle   = "The Biography of Frederick Law Olmstead"
+ * 	Full title = "A Clearing in the Distance: The Biography of Frederick Law Olmstead"
+ * Example:
+ *	Main title = "What Makes a Good Team Player?"
+ *	Subtitle   = "Personality and Team Effectiveness"
+ *	Full title = "What Makes a Good Team Player? Personality and Team Effectiveness"
+ */
+void
+title_combine( str *fullttl, str *mainttl, str *subttl )
+{
+	str_empty( fullttl );
+
+	if ( !mainttl ) return;
+
+	str_strcpy( fullttl, mainttl );
+
+	if ( subttl ) {
+		if ( str_has_value( mainttl ) ) {
+			if ( mainttl->data[ mainttl->len - 1 ] != '?' && mainttl->data[ mainttl->len - 1] != ':' )
+				str_strcatc( fullttl, ": " );
+			else
+				str_strcatc( fullttl, " " );
+		}
+		str_strcat( fullttl, subttl );
+	}
+}
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-2014
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL verison 2
  *
@@ -11,10 +11,10 @@
 #ifndef TITLE_H
 #define TITLE_H
 
-#include "newstr.h"
+#include "str.h"
 #include "fields.h"
 
-extern int title_process( fields *info, char *tag, char *data, int level,
-		unsigned char nosplittitle );
+int  title_process( fields *info, char *tag, char *data, int level, unsigned char nosplittitle );
+void title_combine( str *fullttl, str *mainttl, str *subttl );
 
 #endif
diff --git a/bibutils/unicode.c b/bibutils/unicode.c
--- a/bibutils/unicode.c
+++ b/bibutils/unicode.c
@@ -317,13 +317,13 @@
 }
 
 unsigned short
-unicode_utf8_classify_newstr( newstr *s )
+unicode_utf8_classify_str( str *s )
 {
 	unsigned int unicode_character, pos = 0;
 	unsigned short value = 0;
 	int n;
 	while ( pos < s->len ) {
-		unicode_character = utf8_decode( s->data, &pos );
+		unicode_character = utf8_decode( str_cstr( s ), &pos );
 		n = unicode_find( unicode_character );
 		if ( n==-1 ) value |= UNICODE_SYMBOL;
 		else value |= unicodeinfo[n].info;
diff --git a/bibutils/unicode.h b/bibutils/unicode.h
--- a/bibutils/unicode.h
+++ b/bibutils/unicode.h
@@ -1,14 +1,14 @@
 /*
  * unicode.h
  *
- * Copyright (c) Chris Putnam 2004-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Program and source code released under the GPL version 2
  */
 #ifndef UNICODE_H
 #define UNICODE_H
 
-#include "newstr.h"
+#include "str.h"
 
 #define UNICODE_SYMBOL    (1)
 #define UNICODE_UPPER     (2) /* Uppercase letter */
@@ -17,6 +17,6 @@
 #define UNICODE_MIXEDCASE ( UNICODE_UPPER | UNICODE_LOWER )
 
 extern unsigned short unicode_utf8_classify( char *p );
-extern unsigned short unicode_utf8_classify_newstr( newstr *s );
+extern unsigned short unicode_utf8_classify_str( str *s );
 
 #endif
diff --git a/bibutils/url.c b/bibutils/url.c
new file mode 100644
--- /dev/null
+++ b/bibutils/url.c
@@ -0,0 +1,313 @@
+/*
+ * url.c
+ *
+ * doi_to_url()
+ * Handle outputing DOI as a URL (Endnote and RIS formats)
+ *     1) Append https://doi.org as necessary
+ *     2) Check for overlap with pre-existing URL for the DOI
+ *
+ * is_doi()
+ * Check for DOI buried in another field.
+ *
+ * Copyright (c) Chris Putnam 2008-2017
+ *
+ * Source code released under the GPL version 2
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "bibutils.h"
+#include "url.h"
+
+static void
+construct_url( char *prefix, str *id, str *id_url, char sep )
+{
+	if ( !strncasecmp( str_cstr( id ), "http:", 5 ) )
+		str_strcpy( id_url, id );
+	else {
+		str_strcpyc( id_url, prefix );
+		if ( sep!='\0' ) {
+			if ( id->data[0]!=sep ) str_addchar( id_url, sep );
+		}
+		str_strcat( id_url, id );
+	}
+}
+
+static int
+url_exists( fields *f, char *urltag, str *doi_url )
+{
+	int i, n;
+	if ( urltag ) {
+		n = fields_num( f );
+		for ( i=0; i<n; ++i ) {
+			if ( strcmp( fields_tag( f, i, FIELDS_CHRP ), urltag ) ) continue;
+			if ( strcmp( fields_value( f, i, FIELDS_CHRP ), str_cstr( doi_url ) ) ) continue;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static void
+xxx_to_url( fields *f, int n, char *http_prefix, char *urltag, str *xxx_url, char sep )
+{
+	str_empty( xxx_url );
+	construct_url( http_prefix, fields_value( f, n, FIELDS_STRP ), xxx_url, sep );
+	if ( url_exists( f, urltag, xxx_url ) )
+		str_empty( xxx_url );
+}
+void
+doi_to_url( fields *f, int n, char *urltag, str *url )
+{
+	xxx_to_url( f, n, "https://doi.org", urltag, url, '/' );
+}
+void
+jstor_to_url( fields *f, int n, char *urltag, str *url )
+{
+	xxx_to_url( f, n, "http://www.jstor.org/stable", urltag, url, '/' );
+}
+void
+pmid_to_url( fields *f, int n, char *urltag, str *url )
+{
+	xxx_to_url( f, n, "http://www.ncbi.nlm.nih.gov/pubmed", urltag, url, '/' );
+}
+void
+pmc_to_url( fields *f, int n, char *urltag, str *url )
+{
+	xxx_to_url( f, n, "http://www.ncbi.nlm.nih.gov/pmc/articles", urltag, url, '/' );
+}
+void
+arxiv_to_url( fields *f, int n, char *urltag, str *url )
+{
+	xxx_to_url( f, n, "http://arxiv.org/abs", urltag, url, '/' );
+}
+void
+mrnumber_to_url( fields *f, int n, char *urltag, str *url )
+{
+	xxx_to_url( f, n, "http://www.ams.org/mathscinet-getitem?mr=", urltag, url, '\0' );
+}
+
+/* Rules for the pattern:
+ *   '#' = number
+ *   isalpha() = match precisely (matchcase==1) or match regardless of case
+ *   	(matchcase==0)
+ *   all others must match precisely
+ */
+static int
+string_pattern( char *s, char *pattern, int matchcase )
+{
+	int patlen, match, i;
+	patlen = strlen( pattern );
+	if ( strlen( s ) < patlen ) return 0; /* too short */
+	for ( i=0; i<patlen; ++i ) {
+		match = 0;
+		if ( pattern[i]=='#' ) {
+			if ( isdigit( (unsigned char)s[i] ) ) match = 1;
+		} else if ( !matchcase && isalpha( (unsigned char)pattern[i] ) ) {
+			if ( tolower((unsigned char)pattern[i])==tolower((unsigned char)s[i])) match = 1;
+		} else {
+			if ( pattern[i] == s[i] ) match = 1;
+		}
+		if ( !match ) return 0;
+	}
+	return 1;
+}
+
+/* science direct is now doing "M3  - doi: DOI: 10.xxxx/xxxxx" */
+int
+is_doi( char *s )
+{
+	if ( string_pattern( s, "##.####/", 0 ) ) return 0;
+	if ( string_pattern( s, "doi:##.####/", 0 ) ) return 4;
+	if ( string_pattern( s, "doi: ##.####/", 0 ) ) return 5;
+	if ( string_pattern( s, "doi: DOI: ##.####/", 0 ) ) return 10;
+	return -1;
+}
+
+/* determine if string has the header of a Universal Resource Identifier
+ *
+ * returns -1, if not true
+ * returns offset that skips over the URI scheme, if true
+ */
+int
+is_uri_remote_scheme( char *p )
+{
+	char *scheme[]   = { "http:", "https:", "ftp:", "git:", "gopher:" };
+	int  schemelen[] = { 5,       6,        4,      4,      7         };
+        int i, nschemes = sizeof( scheme ) / sizeof( scheme[0] );
+        for ( i=0; i<nschemes; ++i ) {
+                if ( !strncasecmp( p, scheme[i], schemelen[i] ) ) return schemelen[i];
+        }
+        return -1;
+}
+
+int
+is_reference_database( char *p )
+{
+	char *scheme[]   = { "arXiv:", "pubmed:", "medline:", "isi:" };
+	int  schemelen[] = { 6,        7,         8,          4      };
+        int i, nschemes = sizeof( scheme ) / sizeof( scheme[0] );
+        for ( i=0; i<nschemes; ++i ) {
+                if ( !strncasecmp( p, scheme[i], schemelen[i] ) ) return schemelen[i];
+        }
+        return -1;
+}
+
+/* many fields have been abused to embed URLs, DOIs, etc. */
+int
+is_embedded_link( char *s )
+{
+	if ( is_uri_remote_scheme( s )  != -1 ) return 1;
+	if ( is_reference_database( s ) != -1 ) return 1;
+	if ( is_doi( s ) !=-1 ) return 1;
+	return 0;
+}
+
+typedef struct url_t {
+	char *tag;
+	char *prefix;
+	int offset;
+} url_t;
+
+static url_t prefixes[] = {
+	/*              00000000001111111112222222222333333333344444444445 */
+	/*              12345678901234567890123456789012345678901234567890 */
+	{ "ARXIV",     "http://arxiv.org/abs/",                     21 },
+	{ "DOI",       "https://doi.org/",                          16 },
+	{ "JSTOR",     "http://www.jstor.org/stable/",              28 },
+	{ "MRNUMBER",  "http://www.ams.org/mathscinet-getitem?mr=", 41 },
+	{ "PMID",      "http://www.ncbi.nlm.nih.gov/pubmed/",       35 },
+	{ "PMC",       "http://www.ncbi.nlm.nih.gov/pmc/articles/", 41 },
+	{ "ISIREFNUM", "isi:",                                       4 },
+};
+static int nprefixes = sizeof( prefixes ) / sizeof( prefixes[0] );
+
+/* do not add, but recognize */
+static url_t extraprefixes[] = {
+	/*              00000000001111111112222222222333333333344444444445 */
+	/*              12345678901234567890123456789012345678901234567890 */
+	{ "ARXIV",     "arXiv:",                                     6 },
+	{ "DOI",       "http://dx.doi.org/",                        18 },
+	{ "JSTOR",     "jstor:",                                     6 },
+	{ "PMID",      "pmid:",                                      5 },
+	{ "PMID",      "pubmed:",                                    7 },
+	{ "PMC",       "pmc:",                                       4 },
+	{ "URL",       "\\urllink",                                  8 },
+	{ "URL",       "\\url",                                      4 },
+};
+static int nextraprefixes = sizeof( extraprefixes ) / sizeof( extraprefixes[0] );
+
+static int
+find_prefix( char *s, url_t *p, int np )
+{
+	int i;
+
+	for ( i=0; i<np; ++i )
+		if ( !strncmp( p[i].prefix, s, p[i].offset ) ) return i;
+
+	return -1;
+}
+
+int
+urls_split_and_add( char *value_in, fields *out, int lvl_out )
+{
+	int n, fstatus, status = BIBL_OK;
+	char *tag = "URL";
+	int offset = 0;
+
+	n = find_prefix( value_in, prefixes, nprefixes );
+	if ( n!=-1 ) {
+		tag    = prefixes[n].tag;
+		offset = prefixes[n].offset;
+	} else {
+		n = find_prefix( value_in, extraprefixes, nextraprefixes );
+		if ( n!=-1 ) {
+			tag    = extraprefixes[n].tag;
+			offset = extraprefixes[n].offset;
+		}
+	}
+
+	fstatus = fields_add( out, tag, &(value_in[offset]), lvl_out );
+	if ( fstatus!=FIELDS_OK ) status = BIBL_ERR_MEMERR;
+
+	return status;
+}
+
+/* urls_add_type()
+ *
+ * Append urls of a specific type with a specific prefix (which can be empty).
+ * We don't allow duplications here.
+ *
+ */
+static int
+urls_merge_and_add_type( fields *out, char *tag_out, int lvl_out, char *prefix, vplist *values )
+{
+	int fstatus, status = BIBL_OK;
+	vplist_index i;
+	str url;
+
+	str_init( &url );
+
+	for ( i=0; i<values->n; ++i ) {
+		str_strcpyc( &url, prefix );
+		str_strcatc( &url, ( char * ) vplist_get( values, i ) );
+		fstatus = fields_add( out, tag_out, str_cstr( &url ), lvl_out );
+		if ( fstatus!=FIELDS_OK ) {
+			status = BIBL_ERR_MEMERR;
+			goto out;
+		}
+
+	}
+out:
+	str_free( &url );
+	return status;
+}
+
+/*
+ * urls_merge_and_add()
+ *
+ * Append urls of types controlled by the list type and automatically append appropriate
+ * prefixes. If no prefix is found for the entry, don't add one (e.g. "URL" entries).
+ *
+ * Control of the types to be added by list type is necessary as some reference formats
+ * like bibtex ought to do special things with DOI, ARXIV, MRNUMBER, and the like.
+ */
+int
+urls_merge_and_add( fields *in, int lvl_in, fields *out, char *tag_out, int lvl_out, slist *types )
+{
+	int i, j, status = BIBL_OK;
+	char *tag, *prefix, *empty="";
+	vplist a;
+
+	vplist_init( &a );
+
+	for ( i=0; i<types->n; ++i ) {
+
+		tag = slist_cstr( types, i );
+
+		/* ...look for data of requested type; if not found skip */
+		vplist_empty( &a );
+		fields_findv_each( in, lvl_in, FIELDS_CHRP, &a, tag );
+		if ( a.n==0 ) continue;
+
+		/* ...find the prefix (if present) */
+		prefix = empty;
+		for ( j=0; j<nprefixes; ++j ) {
+			if ( !strcmp( prefixes[j].tag, tag ) ) {
+				prefix = prefixes[j].prefix;
+				break; /* take the first prefix in the list */
+			}
+		}
+
+		/* ...append all data of this type */
+		status = urls_merge_and_add_type( out, tag_out, lvl_out, prefix, &a );
+		if ( status!=BIBL_OK ) goto out;
+	}
+
+out:
+	vplist_free( &a );
+
+	return status;
+}
diff --git a/bibutils/url.h b/bibutils/url.h
new file mode 100644
--- /dev/null
+++ b/bibutils/url.h
@@ -0,0 +1,30 @@
+/*
+ * url.h
+ *
+ * Copyright (c) Chris Putnam 2004-2017
+ *
+ * Source code released under the GPL version 2
+ *
+ */
+#ifndef URL_H
+#define URL_H
+
+#include "slist.h"
+#include "fields.h"
+
+int is_doi( char *s );
+int is_uri_remote_scheme( char *p );
+int is_embedded_link( char *s );
+
+void doi_to_url( fields *info, int n, char *urltag, str *doi_url );
+void pmid_to_url( fields *info, int n, char *urltag, str *pmid_url );
+void pmc_to_url( fields *info, int n, char *urltag, str *pmid_url );
+void arxiv_to_url( fields *info, int n, char *urltag, str *arxiv_url );
+void jstor_to_url( fields *info, int n, char *urltag, str *jstor_url );
+void mrnumber_to_url( fields *info, int n, char *urltag, str *jstor_url );
+
+int urls_merge_and_add( fields *in, int lvl_in, fields *out, char *tag_out, int lvl_out, slist *types );
+int urls_split_and_add( char *value_in, fields *out, int lvl_out );
+
+
+#endif
diff --git a/bibutils/utf8.c b/bibutils/utf8.c
--- a/bibutils/utf8.c
+++ b/bibutils/utf8.c
@@ -1,12 +1,13 @@
 /*
  * utf8.c
  *
- * Copyright (c) Chris Putnam 2004-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
  */
 #include <stdio.h>
+#include <string.h>
 #include "utf8.h"
 
 /* UTF-8 encoding
@@ -146,5 +147,42 @@
 	nc = utf8_encode( 0xFEFF, code );
 	for ( i=0; i<nc; ++i )
 		fprintf(outptr,"%c",code[i]);
+}
+
+int
+utf8_is_bom( char *p )
+{
+	unsigned char *up = ( unsigned char * ) p;
+
+	/* ...if null-terminated string is too short, we're ok */
+	if ( up[0]!=0xEF ) return 0;
+	if ( up[1]!=0xBB ) return 0;
+	if ( up[2]!=0xBF ) return 0;
+
+	return 1;
+}
+
+/* utf8_is_emdash()
+ *
+ *emdash = 0xE2 (-30) 0x80 (-128) 0x94 (-108)
+ */
+int
+utf8_is_emdash( char *p )
+{
+	const char emdash[3] = { -30, -128, -108 };
+	if ( strncmp( p, emdash, 3 ) ) return 0;
+	return 1;
+}
+
+/* utf8_is_endash()
+ *
+ * endash = 0xE2 (-30) 0x80 (-128) 0x93 (-109)
+ */
+int
+utf8_is_endash( char *p )
+{
+	const char endash[3] = { -30, -128, -109 };
+	if ( strncmp( p, endash, 3 ) ) return 0;
+	return 1;
 }
 
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-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
@@ -11,10 +11,12 @@
 
 #include <stdio.h>
 
-extern int utf8_encode( unsigned int value, unsigned char out[6] );
-extern void utf8_encode_str( unsigned int value, char outstr[7] );
-extern unsigned int utf8_decode( char *s, unsigned int *pi );
-extern void utf8_writebom( FILE *outptr );
-
+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 );
+void         utf8_writebom( FILE *outptr );
+int          utf8_is_bom( char *p );
+int          utf8_is_emdash( char *p );
+int          utf8_is_endash( char *p );
 
 #endif
diff --git a/bibutils/vplist.c b/bibutils/vplist.c
--- a/bibutils/vplist.c
+++ b/bibutils/vplist.c
@@ -1,9 +1,9 @@
 /*
  * vplist.c
  *
- * Version: 4/08/2013
+ * Version: 1/9/2017
  *
- * Copyright (c) Chris Putnam 2011-2013
+ * Copyright (c) Chris Putnam 2011-2017
  *
  * Source code released under the GPL version 2
  *
@@ -11,9 +11,19 @@
  *
  */
 #include <stdlib.h>
-#include <assert.h>
 #include "vplist.h"
 
+/* Do not use asserts in VPLIST_NOASSERT defined */
+#ifdef VPLIST_NOASSERT
+#define NDEBUG
+#endif
+#include <assert.h>
+
+#define VPLIST_MINALLOC (20)
+
+#define VPLIST_EXACT_SIZE  (0)
+#define VPLIST_DOUBLE_SIZE (1)
+
 void
 vplist_init( vplist *vpl )
 {
@@ -31,186 +41,347 @@
 	return vpl;
 }
 
-int
-vplist_find( vplist *vpl, void *v )
+static inline int
+vplist_alloc( vplist *vpl, vplist_index alloc )
 {
-	int i;
-	assert( vpl );
-	for ( i=0; i<vpl->n; ++i )
-		if ( vpl->data[i]==v ) return i;
-	return -1;
+	vpl->data = ( void ** ) malloc( sizeof( void * ) * alloc );
+	if ( !vpl->data ) return VPLIST_MEMERR;
+
+	vpl->max = alloc;
+	vpl->n = 0;
+
+	return VPLIST_OK;
 }
 
-int
-vplist_copy( vplist *to, vplist *from )
+static inline int
+vplist_realloc( vplist *vpl, vplist_index alloc )
 {
-	int i;
-	assert( to );
-	assert( from );
-	if ( from->n > to->max ) {
-		if ( to->max ) free( to->data );
-		to->data = ( void ** ) malloc( sizeof( void * ) * from->n );
-		if ( !to->data ) return 0;
-		to->max = from->n;
+	void **more;
+
+	more = ( void ** ) realloc( vpl->data, sizeof( void * ) * alloc );
+	if ( !more ) return VPLIST_MEMERR;
+
+	vpl->data = more;
+	vpl->max  = alloc;
+
+	return VPLIST_OK;
+}
+
+/* vplist_ensure_space( vpl, n, mode )
+ *
+ *    Makes sure that vplist can hold at least n members, allocating memory if required.
+ *
+ *    mode
+ *       - Can either be VPLIST_DOUBLE_SIZE or VPLIST_EXACT_SIZE.
+ *       - If VPLIST_EXACT_SIZE and current size < n, size will be exactly n.
+ *       - If VPLIST_DOUBLE_SIZE and current size < n, size will be doubled (or VPLIST_MINALLOC 
+ *         if the vplist is empty) or n, whichever is bigger.
+ *
+ *    Returns VPLIST_OK or VPLIST_MEMERR.
+ */
+static int
+vplist_ensure_space( vplist *vpl, vplist_index n, unsigned char mode )
+{
+	vplist_index alloc = n;
+	int status = VPLIST_OK;
+
+	if ( vpl->max == 0 ) {
+		if ( mode == VPLIST_DOUBLE_SIZE && alloc < VPLIST_MINALLOC ) alloc = VPLIST_MINALLOC;
+		status = vplist_alloc( vpl, alloc );
 	}
-	for ( i=0; i<from->n; ++i )
-		to->data[i] = from->data[i];
-	to->n = from->n;
-	return 1;
+
+	else if ( vpl->max < n ) {
+		if ( mode == VPLIST_DOUBLE_SIZE && alloc < 2 * vpl->max ) alloc = 2 * vpl->max;
+		status = vplist_realloc( vpl, alloc );
+	}
+
+	return status;
 }
 
 int
-vplist_append( vplist *to, vplist *from )
+vplist_copy( vplist *to, vplist *from )
 {
-	int i, ok;
+	vplist_index i;
+	int status;
+
 	assert( to );
 	assert( from );
-	for ( i=0; i<from->n; ++i ) {
-		ok = vplist_add( to, from->data[i] );
-		if ( !ok ) return 0;
+
+	status = vplist_ensure_space( to, from->n, VPLIST_EXACT_SIZE );
+
+	if ( status == VPLIST_OK ) {
+
+		for ( i=0; i<from->n; ++i )
+			to->data[i] = from->data[i];
+		to->n = from->n;
+
 	}
-	return 1;
-}
 
-static int
-vplist_validindex( vplist *vpl, int n )
-{
-	if ( n < 0 || n >= vpl->n ) return 0;
-	return 1;
+	return status;
 }
 
-static int
-vplist_alloc( vplist *vpl )
+int
+vplist_fill( vplist *vpl, vplist_index n, void *v )
 {
-	int alloc = 20;
-	vpl->data = ( void ** ) malloc( sizeof( void * ) * alloc );
-	if ( !vpl->data ) return 0;
-	vpl->max = alloc;
-	vpl->n = 0;
-	return 1;
-}
+	vplist_index i;
+	int status;
 
-static int
-vplist_realloc( vplist *vpl )
-{
-	void **more;
-	int alloc = vpl->max * 2;
-	more = ( void ** ) realloc( vpl->data, sizeof( void * ) * alloc );
-	if ( !more ) return 0;
-	vpl->data = more;
-	vpl->max = alloc;
-	return 1;
+	assert( vpl );
+
+	status = vplist_ensure_space( vpl, n, VPLIST_EXACT_SIZE );
+
+	if ( status == VPLIST_OK ) {
+
+		for ( i=0; i<n; ++i )
+			vpl->data[i] = v;
+		vpl->n = n;
+
+	}
+
+	return status;
 }
 
 int
 vplist_add( vplist *vpl, void *v )
 {
-	int ok = 1;
+	int status;
 
 	assert( vpl );
 
-	/* ensure sufficient space */
-	if ( vpl->max==0 ) ok = vplist_alloc( vpl );
-	else if ( vpl->n >= vpl->max ) ok = vplist_realloc( vpl );
+	status = vplist_ensure_space( vpl, vpl->n + 1, VPLIST_DOUBLE_SIZE );
 
-	if ( ok ) {
+	if ( status == VPLIST_OK ) {
+
 		vpl->data[vpl->n] = v;
 		vpl->n++;
+
 	}
 
-	return ok;
+	return status;
 }
 
-void *
-vplist_get( vplist *vpl, int n )
+int
+vplist_insert_list( vplist *vpl, vplist_index pos, vplist *add )
 {
+	vplist_index i;
+	int status;
+
 	assert( vpl );
-	if ( !vplist_validindex( vpl, n ) ) return NULL;
-	return vpl->data[ n ];
+	assert( add );
+	assert( pos <= vpl->n );
+
+	/* nothing to do here */
+	if ( add->n < 1 ) return VPLIST_OK;
+
+	status = vplist_ensure_space( vpl, vpl->n + add->n, VPLIST_DOUBLE_SIZE );
+
+	if ( status == VPLIST_OK ) {
+
+		for ( i=vpl->n-1; i>=pos; --i )
+			vpl->data[i+add->n] = vpl->data[i];
+
+		for ( i=0; i<add->n; ++i )
+			vpl->data[pos+i] = add->data[i];
+
+		vpl->n += add->n;
+	}
+
+	return status;
 }
 
-void
-vplist_set( vplist *vpl, int n, void *v )
+int
+vplist_append( vplist *vpl, vplist *add )
 {
+	vplist_index i;
+	int status;
+
 	assert( vpl );
-	if ( !vplist_validindex( vpl, n ) ) return;
-	vpl->data[ n ] = v;
+	assert( add );
+
+	status = vplist_ensure_space( vpl, vpl->n + add->n, VPLIST_DOUBLE_SIZE );
+
+	if ( status == VPLIST_OK ) {
+
+		for ( i=0; i<add->n; ++i )
+			vpl->data[ vpl->n + i ] = add->data[i];
+
+		vpl->n += add->n;
+
+	}
+
+	return status;
 }
 
+static void
+vplist_freemembers( vplist *vpl, vplist_ptrfree vpf )
+{
+	vplist_index i;
+	void *v;
+	for ( i=0; i<vpl->n; ++i ) {
+		v = vplist_get( vpl, i );
+		if ( v ) (*vpf)( v );
+	}
+}
+
 void
-vplist_remove( vplist *vpl, int n )
+vplist_emptyfn( vplist *vpl, vplist_ptrfree vpf )
 {
-	int i;
 	assert( vpl );
-	if ( !vplist_validindex( vpl, n ) ) return;
-	for ( i=n+1; i<vpl->n; ++i )
-		vpl->data[ i-1 ] = vpl->data[ i ];
-	vpl->n -= 1;
+	if ( vpf ) vplist_freemembers( vpl, vpf );
+	vpl->n = 0;
 }
 
 void
-vplist_removevp( vplist *vpl, void *v )
+vplist_empty( vplist *vpl )
 {
-	int n;
+	vplist_emptyfn( vpl, NULL );
+}
+
+void
+vplist_freefn( vplist *vpl, vplist_ptrfree vpf )
+{
 	assert( vpl );
-	do {
-		n = vplist_find( vpl, v );
-		if ( n!=-1 ) vplist_remove( vpl, n );
-	} while ( n!=-1 );
+	if ( vpf ) vplist_freemembers( vpl, vpf );
+	if ( vpl->data ) free( vpl->data );
+	vplist_init( vpl );
 }
 
-static void
-vplist_freemembers( vplist *vpl, vplist_ptrfree vpf )
+void
+vplist_free( vplist *vpl )
 {
-	int i;
-	for ( i=0; i<vpl->n; ++i )
-		(*vpf)( vplist_get( vpl, i ) );
+	vplist_freefn( vpl, NULL );
 }
 
 void
-vplist_empty( vplist *vpl )
+vplist_deletefn( vplist **vpl, vplist_ptrfree vpf )
 {
-	assert( vpl );
-	vpl->n = 0;
+	vplist_freefn( *vpl, vpf );
+	free( *vpl );
+	*vpl = NULL;
 }
 
 void
-vplist_emptyfn( vplist *vpl, vplist_ptrfree vpf )
+vplist_delete( vplist **vpl )
 {
+	vplist_deletefn( vpl, NULL );
+}
+
+static inline int
+vplist_validindex( vplist *vpl, vplist_index n )
+{
+	if ( n < 0 || n >= vpl->n ) return 0;
+	return 1;
+}
+
+void *
+vplist_get( vplist *vpl, vplist_index n )
+{
 	assert( vpl );
-	vplist_freemembers( vpl, vpf );
-	vplist_empty( vpl );
+	if ( !vplist_validindex( vpl, n ) ) return NULL;
+	return vpl->data[ n ];
 }
 
 void
-vplist_free( vplist *vpl )
+vplist_set( vplist *vpl, vplist_index n, void *v )
 {
 	assert( vpl );
-	if ( vpl->data ) free( vpl->data );
-	vplist_init( vpl );
+	assert( vplist_validindex( vpl, n ) );
+	vpl->data[ n ] = v;
 }
 
+int
+vplist_find( vplist *vpl, void *v )
+{
+	vplist_index i;
+	assert( vpl );
+	for ( i=0; i<vpl->n; ++i )
+		if ( vpl->data[i]==v ) return i;
+	return -1;
+}
+
 void
-vplist_freefn( vplist *vpl, vplist_ptrfree vpf )
+vplist_swap( vplist *vpl, vplist_index n1, vplist_index n2 )
 {
+	void *tmp;
+
 	assert( vpl );
-	vplist_freemembers( vpl, vpf );
-	vplist_free( vpl );
+	assert( vplist_validindex( vpl, n1 ) );
+	assert( vplist_validindex( vpl, n2 ) );
+
+	tmp           = vpl->data[n1];
+	vpl->data[n1] = vpl->data[n2];
+	vpl->data[n2] = tmp;
 }
 
+int
+vplist_removefn( vplist *vpl, vplist_index n, vplist_ptrfree vpf )
+{
+	vplist_index i;
+
+	assert( vpl );
+	assert( vplist_validindex( vpl, n ) );
+
+	if ( vpf ) (*vpf)( vplist_get( vpl, n ) );
+
+	for ( i=n+1; i<vpl->n; ++i )
+		vpl->data[ i-1 ] = vpl->data[ i ];
+	vpl->n -= 1;
+
+	return 1;
+}
+
+int
+vplist_remove( vplist *vpl, vplist_index n )
+{
+	return vplist_removefn( vpl, n, NULL );
+}
+
+int
+vplist_removevpfn( vplist *vpl, void *v, vplist_ptrfree vpf )
+{
+	vplist_index n;
+	int count = 0;
+
+	assert( vpl );
+
+	do {
+		n = vplist_find( vpl, v );
+		if ( vplist_found( vpl, n ) ) {
+			vplist_removefn( vpl, n, vpf );
+			count++;
+		}
+	} while ( vplist_found( vpl, n ) );
+
+	return count;
+}
+
+int
+vplist_removevp( vplist *vpl, void *v )
+{
+	return vplist_removevpfn( vpl, v, NULL );
+}
+
 void
-vplist_destroy( vplist **vpl )
+vplist_remove_rangefn( vplist *vpl, vplist_index start, vplist_index endplusone, vplist_ptrfree vpf )
 {
-	assert( *vpl );
-	vplist_free( *vpl );
-	free( *vpl );
-	*vpl = NULL;
+	vplist_index i, n;
+
+	assert( endplusone <= vpl->n );
+	assert( endplusone > start );
+
+	n = endplusone - start;
+	if ( vpf ) {
+		for ( i=start; i<endplusone; ++i )
+			(*vpf)( vplist_get( vpl, i ) );
+	}
+	for ( i=endplusone; i<vpl->n; ++i ) {
+		vpl->data[i-n] = vpl->data[i];
+	}
+	vpl->n -= n;
 }
 
 void
-vplist_destroyfn( vplist **vpl, vplist_ptrfree vpf )
+vplist_remove_range( vplist *vpl, vplist_index start, vplist_index endplusone )
 {
-	assert( *vpl );
-	vplist_freemembers( *vpl, vpf );
-	vplist_destroy( vpl );
+	vplist_remove_rangefn( vpl, start, endplusone, NULL );
 }
diff --git a/bibutils/vplist.h b/bibutils/vplist.h
--- a/bibutils/vplist.h
+++ b/bibutils/vplist.h
@@ -1,10 +1,12 @@
 /*
  * vplist.h
  *
- * Version: 4/08/2013
+ * generic container to hold a list of pointers to void
  *
- * Copyright (c) Chris Putnam 2011-2013
+ * Version: 1/9/2017
  *
+ * Copyright (c) Chris Putnam 2011-2017
+ *
  * Source code released under the GPL version 2
  *
  */
@@ -12,25 +14,41 @@
 #ifndef VPLIST_H
 #define VPLIST_H
 
-/* vplist = void pointer list (generic container struct)
- */
+#define VPLIST_MEMERR (-1)
+#define VPLIST_OK     (0)
+
+typedef int vplist_index;
+
 typedef struct vplist {
-	int n, max;
+	vplist_index n, max;
 	void **data;
 } vplist;
 
+#define vplist_found( vpl, n ) ( n!=-1 )
+#define vplist_notfound( vpl, n ) ( n==-1 )
+
 typedef void (*vplist_ptrfree)(void*);
 
-extern void     vplist_init( vplist *vpl );
-extern vplist * vplist_new( void );
-extern int      vplist_add( vplist *vpl, void *v );
-extern int      vplist_copy( vplist *to, vplist *from );
-extern int      vplist_append( vplist *to, vplist *from );
-extern void *   vplist_get( vplist *vpl, int n );
-extern void     vplist_set( vplist *vpl, int n, void *v );
-extern void     vplist_remove( vplist *vpl, int n );
-extern void     vplist_removevp( vplist *vpl, void *v );
-extern int      vplist_find( vplist *vpl, void *v );
+vplist *     vplist_new( void );
+
+void   vplist_init          ( vplist *vpl );
+int    vplist_add           ( vplist *vpl, void *v );
+int    vplist_fill          ( vplist *vpl, vplist_index n, void *v );
+int    vplist_copy          ( vplist *to,  vplist *from );
+int    vplist_append        ( vplist *vpl, vplist *add );
+int    vplist_insert_list   ( vplist *vpl, vplist_index pos, vplist *add );
+void * vplist_get           ( vplist *vpl, vplist_index n );
+void   vplist_set           ( vplist *vpl, vplist_index n, void *v );
+void   vplist_swap          ( vplist *vpl, vplist_index n1, vplist_index n2 );
+int    vplist_remove        ( vplist *vpl, vplist_index n );
+int    vplist_removefn      ( vplist *vpl, vplist_index n, vplist_ptrfree vpf );
+int    vplist_removevp      ( vplist *vpl, void *v );
+int    vplist_removevpfn    ( vplist *vpl, void *v, vplist_ptrfree vpf );
+void   vplist_remove_rangefn( vplist *vpl, vplist_index start, vplist_index endplusone, vplist_ptrfree vpf );
+void   vplist_remove_range  ( vplist *vpl, vplist_index start, vplist_index endplusone );
+
+vplist_index vplist_find( vplist *vpl, void *v );
+
 /*
  * vplist_empty does not free space
  *
@@ -49,20 +67,20 @@
  *
  * vplist_emptyfn( &vpl, free );
  */
-extern void   vplist_empty( vplist *vpl );
-extern void   vplist_emptyfn( vplist *vpl, vplist_ptrfree fn );
+void   vplist_empty  ( vplist *vpl );
+void   vplist_emptyfn( vplist *vpl, vplist_ptrfree fn );
 /*
  * vplist_free frees the space for the data array of void * elements.
  *
  * if members require their own free calls, then call vplist_freefn()
  */
-extern void vplist_free( vplist *vpl );
-extern void vplist_freefn( vplist *vpl, vplist_ptrfree fn );
+void vplist_free  ( vplist *vpl );
+void vplist_freefn( vplist *vpl, vplist_ptrfree fn );
 /*
- * vplist_destroy does vplist_free and deallocates the struct
+ * vplist_delete does vplist_free and deallocates the struct
  * vplist * and replaces with NULL.
  */
-extern void vplist_destroy( vplist **vpl );
-extern void vplist_destroyfn( vplist **vpl, vplist_ptrfree fn );
+void vplist_delete  ( vplist **vpl );
+void vplist_deletefn( vplist **vpl, vplist_ptrfree fn );
 
 #endif
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-2013
+ * Copyright (c) Chris Putnam 2010-2017
  *
  * Source code released under the GPL version 2
  *
@@ -9,13 +9,17 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "is_ws.h"
-#include "newstr.h"
-#include "newstr_conv.h"
+#include "str.h"
+#include "str_conv.h"
 #include "fields.h"
 #include "xml.h"
 #include "xml_encoding.h"
-#include "wordin.h"
+#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 );
+
+
 /*****************************************************
  PUBLIC: void wordin_initparams()
 *****************************************************/
@@ -43,8 +47,8 @@
 	p->all      = NULL;
 	p->nall     = 0;
 
-	list_init( &(p->asis) );
-	list_init( &(p->corps) );
+	slist_init( &(p->asis) );
+	slist_init( &(p->corps) );
 
 	if ( !progname ) p->progname = NULL;
 	else p->progname = strdup( progname );
@@ -68,35 +72,35 @@
 	return endptr;
 }
 
-int
-wordin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset )
+static int
+wordin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, str *line, str *reference, int *fcharset )
 {
-	newstr tmp;
+	str tmp;
 	char *startptr = NULL, *endptr;
 	int haveref = 0, inref = 0, file_charset = CHARSET_UNKNOWN, m, type = 1;
-	newstr_init( &tmp );
-	while ( !haveref && newstr_fget( fp, buf, bufsize, bufpos, line ) ) {
-		if ( line->data ) {
+	str_init( &tmp );
+	while ( !haveref && str_fget( fp, buf, bufsize, bufpos, line ) ) {
+		if ( str_cstr( line ) ) {
 			m = xml_getencoding( line );
 			if ( m!=CHARSET_UNKNOWN ) file_charset = m;
 		}
-		if ( line->data ) {
-			startptr = wordin_findstartwrapper( line->data, &type );
+		if ( str_cstr( line ) ) {
+			startptr = wordin_findstartwrapper( str_cstr( line ), &type );
 		}
 		if ( startptr || inref ) {
-			if ( inref ) newstr_strcat( &tmp, line->data );
+			if ( inref ) str_strcat( &tmp, line );
 			else {
-				newstr_strcat( &tmp, startptr );
+				str_strcatc( &tmp, startptr );
 				inref = 1;
 			}
-			endptr = wordin_findendwrapper( tmp.data, type );
+			endptr = wordin_findendwrapper( str_cstr( &tmp ), type );
 			if ( endptr ) {
-				newstr_segcpy( reference, tmp.data, endptr );
+				str_segcpy( reference, str_cstr( &tmp ), endptr );
 				haveref = 1;
 			}
 		}
 	}
-	newstr_free( &tmp );
+	str_free( &tmp );
 	*fcharset = file_charset;
 	return haveref;
 }
@@ -122,13 +126,13 @@
  * Returns BIBL_ERR_MEMERR on memory error, BIBL_OK otherwise.
  */
 static int
-wordin_person_last( xml *node, newstr *name )
+wordin_person_last( xml *node, str *name )
 {
 	while ( node && !xml_tagexact( node, "b:Last" ) )
 		node = node->next;
-	if ( node && node->value->len ) {
-		newstr_strcpy( name, node->value->data );
-		if ( newstr_memerr( name ) ) return BIBL_ERR_MEMERR;
+	if ( node && str_has_value( node->value ) ) {
+		str_strcpyc( name, xml_value( node ) );
+		if ( str_memerr( name ) ) return BIBL_ERR_MEMERR;
 	}
 	return BIBL_OK;
 }
@@ -141,14 +145,14 @@
  * Returns BIBL_ERR_MEMERR on memory error, BIBL_OK otherwise
  */
 static int
-wordin_person_first( xml *node, newstr *name )
+wordin_person_first( xml *node, str *name )
 {
 	for ( ; node; node=node->next ) {
 		if ( !xml_tagexact( node, "b:First" ) ) continue;
-		if ( node->value->len ) {
-			if ( name->len ) newstr_addchar( name, '|' );
-			newstr_strcat( name, node->value->data );
-			if ( newstr_memerr( name ) ) return BIBL_ERR_MEMERR;
+		if ( str_has_value( node->value ) ) {
+			if ( str_has_value( name ) ) str_addchar( name, '|' );
+			str_strcatc( name, xml_value( node ) );
+			if ( str_memerr( name ) ) return BIBL_ERR_MEMERR;
 		}
 	}
 	return BIBL_OK;
@@ -158,9 +162,9 @@
 wordin_person( xml *node, fields *info, char *type )
 {
 	int status, ret = BIBL_OK;
-	newstr name;
+	str name;
 
-	newstr_init( &name );
+	str_init( &name );
 
 	status = wordin_person_last( node, &name );
 	if ( status!=BIBL_OK ) {
@@ -174,10 +178,10 @@
 		goto out;
 	}
 
-	status = fields_add( info, type, name.data, 0 );
+	status = fields_add( info, type, str_cstr( &name ), 0 );
 	if ( status != FIELDS_OK ) ret = BIBL_ERR_MEMERR;
 out:
-	newstr_free( &name );
+	str_free( &name );
 	return ret;
 }
 
@@ -201,42 +205,42 @@
 wordin_pages( xml *node, fields *info )
 {
 	int i, status, ret = BIBL_OK;
-	newstr sp, ep;
+	str sp, ep;
 	char *p;
 
-	newstrs_init( &sp, &ep, NULL );
+	strs_init( &sp, &ep, NULL );
 
-	p = xml_data( node );
+	p = xml_value( node );
 	while ( *p && *p!='-' )
-		newstr_addchar( &sp, *p++ );
-	if ( newstr_memerr( &sp ) ) {
+		str_addchar( &sp, *p++ );
+	if ( str_memerr( &sp ) ) {
 		ret = BIBL_ERR_MEMERR;
 		goto out;
 	}
 
 	if ( *p=='-' ) p++;
 	while ( *p )
-		newstr_addchar( &ep, *p++ );
-	if ( newstr_memerr( &ep ) ) {
+		str_addchar( &ep, *p++ );
+	if ( str_memerr( &ep ) ) {
 		ret = BIBL_ERR_MEMERR;
 		goto out;
 	}
 
-	if ( sp.len ) {
-		status = fields_add( info, "PAGESTART", sp.data, 1 );
+	if ( str_has_value( &sp ) ) {
+		status = fields_add( info, "PAGES:START", str_cstr( &sp ), 1 );
 		if ( status!=FIELDS_OK ) {
 			ret = BIBL_ERR_MEMERR;
 			goto out;
 		}
 	}
 
-	if ( ep.len ) {
+	if ( str_has_value( &ep ) ) {
 		if ( sp.len > ep.len ) {
 			for ( i=sp.len-ep.len; i<sp.len; ++i )
 				sp.data[i] = ep.data[i-sp.len+ep.len];
-			status = fields_add( info, "PAGEEND", sp.data, 1 );
+			status = fields_add( info, "PAGES:STOP", str_cstr( &sp ), 1 );
 		} else
-			status = fields_add( info, "PAGEEND", ep.data, 1 );
+			status = fields_add( info, "PAGES:STOP", str_cstr( &ep ), 1 );
 		if ( status!=FIELDS_OK ) {
 			ret = BIBL_ERR_MEMERR;
 			goto out;
@@ -244,7 +248,7 @@
 	}
 
 out:
-	newstrs_free( &sp, &ep, NULL );
+	strs_free( &sp, &ep, NULL );
 	return ret;
 }
 
@@ -252,28 +256,28 @@
 wordin_reference( xml *node, fields *info )
 {
 	int status, ret = BIBL_OK;
-	if ( xml_hasdata( node ) ) {
+	if ( xml_hasvalue( node ) ) {
 		if ( xml_tagexact( node, "b:Tag" ) ) {
-			status = fields_add( info, "REFNUM", xml_data( node ), 0 );
+			status = fields_add( info, "REFNUM", xml_value( node ), 0 );
 			if ( status!=FIELDS_OK ) ret = BIBL_ERR_MEMERR;
 		} else if ( xml_tagexact( node, "b:SourceType" ) ) {
 		} else if ( xml_tagexact( node, "b:City" ) ) {
-			status = fields_add( info, "ADDRESS", xml_data( node ), 0 );
+			status = fields_add( info, "ADDRESS", xml_value( node ), 0 );
 			if ( status!=FIELDS_OK ) ret = BIBL_ERR_MEMERR;
 		} else if ( xml_tagexact( node, "b:Publisher" ) ) {
-			status = fields_add( info, "PUBLISHER", xml_data( node ), 0 );
+			status = fields_add( info, "PUBLISHER", xml_value( node ), 0 );
 			if ( status!=FIELDS_OK ) ret = BIBL_ERR_MEMERR;
 		} else if ( xml_tagexact( node, "b:Title" ) ) {
-			status = fields_add( info, "TITLE", xml_data( node ), 0 );
+			status = fields_add( info, "TITLE", xml_value( node ), 0 );
 			if ( status!=FIELDS_OK ) ret = BIBL_ERR_MEMERR;
 		} else if ( xml_tagexact( node, "b:JournalName" ) ) {
-			status = fields_add( info, "TITLE", xml_data( node ), 1 );
+			status = fields_add( info, "TITLE", xml_value( node ), 1 );
 			if ( status!=FIELDS_OK ) ret = BIBL_ERR_MEMERR;
 		} else if ( xml_tagexact( node, "b:Volume" ) ) {
-			status = fields_add( info, "VOLUME", xml_data( node ), 1 );
+			status = fields_add( info, "VOLUME", xml_value( node ), 1 );
 			if ( status!=FIELDS_OK ) ret = BIBL_ERR_MEMERR;
 		} else if ( xml_tagexact( node, "b:Comments" ) ) {
-			status = fields_add( info, "NOTES", xml_data( node ), 0 );
+			status = fields_add( info, "NOTES", xml_value( node ), 0 );
 			if ( status!=FIELDS_OK ) ret = BIBL_ERR_MEMERR;
 		} else if ( xml_tagexact( node, "b:Pages" ) ) {
 			ret = wordin_pages( node, info );
@@ -293,14 +297,14 @@
 	int ret = BIBL_OK;
 	if ( xml_tagexact( node, "b:Source" ) ) {
 		if ( node->down ) ret = wordin_reference( node->down, info );
-	} else if ( node->tag->len==0 && node->down ) {
+	} else if ( str_is_empty( node->tag ) && node->down ) {
 		ret = wordin_assembleref( node->down, info );
 	}
 	return ret;
 }
 
-int
-wordin_processf( fields *wordin, char *data, char *filename, long nref )
+static int
+wordin_processf( fields *wordin, char *data, char *filename, long nref, param *p )
 {
 	int status, ret = 1;
 	xml top;
diff --git a/bibutils/wordin.h b/bibutils/wordin.h
deleted file mode 100644
--- a/bibutils/wordin.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * wordin.h
- *
- * Copyright (c) Chris Putnam 2009-2013
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef WORDIN_H
-#define WORDIN_H
-
-#include "newstr.h"
-#include "fields.h"
-#include "reftypes.h"
-#include "bibutils.h"
-
-extern int wordin_readf( FILE *fp, char *buf, int bufsize, int *bufpos, newstr *line, newstr *reference, int *fcharset );
-extern int wordin_processf( fields *wordin, char *data, char *filename, long nref );
-
-extern void wordin_initparams( param *p, const char *progname );
-
-#endif
-
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-2014
+ * Copyright (c) Chris Putnam 2007-2017
  *
  * Source code released under the GPL version 2
  *
@@ -11,11 +11,15 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "newstr.h"
+#include "str.h"
 #include "fields.h"
 #include "utf8.h"
-#include "wordout.h"
+#include "bibformats.h"
 
+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 )
 {
@@ -41,8 +45,9 @@
 }
 
 typedef struct convert {
-	char oldtag[25];
-	char newtag[25];
+	char *oldtag;
+	char *newtag;
+	char *prefix;
 	int  code;
 } convert;
 
@@ -102,24 +107,28 @@
  * fixed output
  */
 static void
-output_fixed( FILE *outptr, char *tag, char *data, int level )
+output_fixed( FILE *outptr, char *tag, char *value, int level )
 {
 	int i;
 	for ( i=0; i<level; ++i ) fprintf( outptr, " " );
-	fprintf( outptr, "<%s>%s</%s>\n", tag, data, tag );
+	fprintf( outptr, "<%s>%s</%s>\n", tag, value, tag );
 }
 
 /* detail output
  *
  */
 static void
-output_item( fields *info, FILE *outptr, char *tag, int item, int level )
+output_item( fields *info, FILE *outptr, char *tag, char *prefix, int item, int level )
 {
 	int i;
 	if ( item==-1 ) return;
 	for ( i=0; i<level; ++i ) fprintf( outptr, " " );
-	fprintf( outptr, "<%s>%s</%s>\n", tag, info->data[item].data, tag );
-	fields_setused( info, item );
+	fprintf( outptr, "<%s>%s%s</%s>\n",
+		tag,
+		prefix,
+		(char*) fields_value( info, item, FIELDS_CHRP ),
+		tag
+	);
 }
 
 static void
@@ -157,7 +166,7 @@
         int i, n;
         for ( i=0; i<nc; ++i ) {
                 n = fields_find( info, c[i].oldtag, c[i].code );
-                if ( n!=-1 ) output_item( info, outptr, c[i].newtag, n, 0 );
+                if ( n!=-1 ) output_item( info, outptr, c[i].newtag, c[i].prefix, n, 0 );
         }
 
 }
@@ -195,11 +204,11 @@
 get_type_from_genre( fields *info )
 {
 	int type = TYPE_UNKNOWN, i, j, level;
-	char *genre;
+	char *genre, *tag;
 	for ( i=0; i<info->n; ++i ) {
-		if ( strcasecmp( info->tag[i].data, "GENRE" ) &&
-			strcasecmp( info->tag[i].data, "NGENRE" ) ) continue;
-		genre = info->data[i].data;
+		tag = (char *) fields_tag( info, i, FIELDS_CHRP );
+		if ( strcasecmp( tag, "GENRE" ) && strcasecmp( tag, "NGENRE" ) ) continue;
+		genre = (char *) fields_value( info, i, FIELDS_CHRP );
 		for ( j=0; j<ngenres; ++j ) {
 			if ( !strcasecmp( genres[j].out, genre ) )
 				type = genres[j].value;
@@ -220,7 +229,7 @@
 			}
 			else if ( !strcasecmp( genre, "conference publication" ) ) {
 				if ( level==0 ) type=TYPE_CONFERENCE;
-				type = TYPE_PROCEEDINGS;
+				else type = TYPE_PROCEEDINGS;
 			}
 			else if ( !strcasecmp( genre, "thesis" ) ) {
 	                        if ( type==TYPE_UNKNOWN ) type=TYPE_THESIS;
@@ -240,11 +249,11 @@
 get_type_from_resource( fields *info )
 {
 	int type = TYPE_UNKNOWN, i;
-	char *resource;
+	char *tag, *resource;
 	for ( i=0; i<info->n; ++i ) {
-		if ( strcasecmp( info->tag[i].data, "GENRE" )!=0 &&
-			strcasecmp( info->tag[i].data, "NGENRE" )!=0 ) continue;
-		resource = info->data[i].data;
+		tag = (char *) fields_tag( info, i, FIELDS_CHRP );
+		if ( strcasecmp( tag, "GENRE" ) && strcasecmp( tag, "NGENRE" ) ) continue;
+		resource = (char *) fields_value( info, i, FIELDS_CHRP );
 		if ( !strcasecmp( resource, "moving image" ) )
 			type = TYPE_FILM;
 	}
@@ -262,43 +271,68 @@
 }
 
 static void
-output_titleinfo( fields *info, FILE *outptr, char *tag, int level )
+output_titlebits( char *mainttl, char *subttl, FILE *outptr )
 {
-	newstr *mainttl = fields_findv( info, level, FIELDS_STRP, "TITLE" );
-	newstr *subttl  = fields_findv( info, level, FIELDS_STRP, "SUBTITLE" );
+	if ( mainttl ) fprintf( outptr, "%s", mainttl );
+	if ( subttl ) {
+		if ( mainttl ) {
+			if ( mainttl[ strlen( mainttl ) - 1 ] != '?' )
+				fprintf( outptr, ": " );
+			else fprintf( outptr, " " );
+		}
+		fprintf( outptr, "%s", subttl );
+	}
+}
+
+static void
+output_titleinfo( char *mainttl, char *subttl, FILE *outptr, char *tag, int level )
+{
 	if ( mainttl || subttl ) {
 		fprintf( outptr, "<%s>", tag );
-		if ( mainttl ) fprintf( outptr, "%s", mainttl->data );
-		if ( subttl ) {
-			if ( mainttl ) {
-				if ( mainttl->len > 0 &&
-				     mainttl->data[mainttl->len-1]!='?' )
-					fprintf( outptr, ": " );
-				else fprintf( outptr, " " );
-			}
-			fprintf( outptr, "%s", subttl->data );
-		}
+		output_titlebits( mainttl, subttl, outptr );
 		fprintf( outptr, "</%s>\n", tag );
 	}
 }
 
 static void
-output_title( fields *info, FILE *outptr, int level )
+output_generaltitle( fields *info, FILE *outptr, char *tag, int level )
 {
-	char *ttl    = fields_findv( info, level, FIELDS_CHRP, "TITLE" );
-	char *subttl = fields_findv( info, level, FIELDS_CHRP, "SUBTITLE" );
-	char *shrttl = fields_findv( info, level, FIELDS_CHRP, "SHORTTITLE" );
+	char *ttl       = fields_findv( info, level, FIELDS_CHRP, "TITLE" );
+	char *subttl    = fields_findv( info, level, FIELDS_CHRP, "SUBTITLE" );
+	char *shrttl    = fields_findv( info, level, FIELDS_CHRP, "SHORTTITLE" );
+	char *shrsubttl = fields_findv( info, level, FIELDS_CHRP, "SHORTSUBTITLE" );
 
-	output_titleinfo( info, outptr, "b:Title", level );
+	if ( ttl ) {
+		output_titleinfo( ttl, subttl, outptr, tag, level );
+	}
+	else if ( shrttl ) {
+		output_titleinfo( shrttl, shrsubttl, outptr, tag, level );
+	}
+}
 
-	/* output shorttitle if it's different from normal title */
-	if ( shrttl ) {
-		if ( !ttl || ( strcmp( shrttl, ttl ) || subttl ) ) {
-			fprintf( outptr,  " <b:ShortTitle>" );
-			fprintf( outptr, "%s", shrttl );
-			fprintf( outptr, "</b:ShortTitle>\n" );
+static void
+output_maintitle( fields *info, FILE *outptr, int level )
+{
+	char *ttl       = fields_findv( info, level, FIELDS_CHRP, "TITLE" );
+	char *subttl    = fields_findv( info, level, FIELDS_CHRP, "SUBTITLE" );
+	char *shrttl    = fields_findv( info, level, FIELDS_CHRP, "SHORTTITLE" );
+	char *shrsubttl = fields_findv( info, level, FIELDS_CHRP, "SHORTSUBTITLE" );
+
+	if ( ttl ) {
+		output_titleinfo( ttl, subttl, outptr, "b:Title", level );
+
+		/* output shorttitle if it's different from normal title */
+		if ( shrttl ) {
+			if ( !ttl || ( strcmp( shrttl, ttl ) || subttl ) ) {
+				fprintf( outptr,  " <b:ShortTitle>" );
+				output_titlebits( shrttl, shrsubttl, outptr );
+				fprintf( outptr, "</b:ShortTitle>\n" );
+			}
 		}
 	}
+	else if ( shrttl ) {
+		output_titleinfo( shrttl, shrsubttl, outptr, "b:Title", level );
+	}
 }
 
 static void
@@ -312,40 +346,39 @@
 static void
 output_name( FILE *outptr, char *p )
 {
-	newstr family, part;
+	str family, part;
 	int n=0, npart=0;
 
-	newstr_init( &family );
-	while ( *p && *p!='|' ) newstr_addchar( &family, *p++ );
+	str_init( &family );
+	while ( *p && *p!='|' ) str_addchar( &family, *p++ );
 	if ( *p=='|' ) p++;
-	if ( family.len ) {
+	if ( str_has_value( &family ) ) {
 		fprintf( outptr, "<b:Person>" );
-		fprintf( outptr, "<b:Last>%s</b:Last>",family.data );
+		fprintf( outptr, "<b:Last>%s</b:Last>", str_cstr( &family ) );
 		n++;
 	}
-	newstr_free( &family );
+	str_free( &family );
 
-	newstr_init( &part );
+	str_init( &part );
 	while ( *p ) {
-		while ( *p && *p!='|' ) newstr_addchar( &part, *p++ );
-		if ( part.len ) {
+		while ( *p && *p!='|' ) str_addchar( &part, *p++ );
+		if ( str_has_value( &part ) ) {
 			if ( n==0 ) fprintf( outptr, "<b:Person>" );
 			if ( npart==0 ) 
-				fprintf( outptr, "<b:First>%s</b:First>",
-					part.data );
-			else fprintf( outptr, "<b:Middle>%s</b:Middle>",
-					part.data );
+				fprintf( outptr, "<b:First>%s</b:First>", str_cstr( &part ) );
+			else
+				fprintf( outptr, "<b:Middle>%s</b:Middle>", str_cstr( &part ) );
 			n++;
 			npart++;
 		}
 		if ( *p=='|' ) {
 			p++;
-			newstr_empty( &part );
+			str_empty( &part );
 		}
 	}
 	if ( n ) fprintf( outptr, "</b:Person>\n" );
 
-	newstr_free( &part );
+	str_free( &part );
 }
 
 
@@ -354,12 +387,12 @@
 #define NAME_CORP (4)
 
 static int
-extract_name_and_info( newstr *outtag, newstr *intag )
+extract_name_and_info( str *outtag, str *intag )
 {
 	int code = NAME;
-	newstr_newstrcpy( outtag, intag );
-	if ( newstr_findreplace( outtag, ":ASIS", "" ) ) code = NAME_ASIS;
-	if ( newstr_findreplace( outtag, ":CORP", "" ) ) code = NAME_CORP;
+	str_strcpy( outtag, intag );
+	if ( str_findreplace( outtag, ":ASIS", "" ) ) code = NAME_ASIS;
+	if ( str_findreplace( outtag, ":CORP", "" ) ) code = NAME_CORP;
 	return code;
 }
 
@@ -367,25 +400,24 @@
 output_name_type( fields *info, FILE *outptr, int level, 
 			char *map[], int nmap, char *tag )
 {
-	newstr ntag;
+	str ntag;
 	int i, j, n=0, code, nfields;
-	newstr_init( &ntag );
+	str_init( &ntag );
 	nfields = fields_num( info );
 	for ( j=0; j<nmap; ++j ) {
 		for ( i=0; i<nfields; ++i ) {
 			code = extract_name_and_info( &ntag, &(info->tag[i]) );
-			if ( strcasecmp( ntag.data, map[j] ) ) continue;
+			if ( strcasecmp( str_cstr( &ntag ), map[j] ) ) continue;
 			if ( n==0 )
 				fprintf( outptr, "<%s><b:NameList>\n", tag );
 			if ( code != NAME )
-				output_name_nomangle( outptr, info->data[i].data );
+				output_name_nomangle( outptr, (char *) fields_value( info, i, FIELDS_CHRP ) );
 			else 
-				output_name( outptr, info->data[i].data );
-			fields_setused( info, i );
+				output_name( outptr, (char *) fields_value( info, i, FIELDS_CHRP ) );
 			n++;
 		}
 	}
-	newstr_free( &ntag );
+	str_free( &ntag );
 	if ( n )
 		fprintf( outptr, "</b:NameList></%s>\n", tag );
 }
@@ -418,11 +450,11 @@
 output_date( fields *info, FILE *outptr, int level )
 {
 	char *year  = fields_findv_firstof( info, level, FIELDS_CHRP,
-			"PARTYEAR", "YEAR", NULL );
+			"PARTDATE:YEAR", "DATE:YEAR", NULL );
 	char *month = fields_findv_firstof( info, level, FIELDS_CHRP,
-			"PARTMONTH", "MONTH", NULL );
+			"PARTDATE:MONTH", "DATE:MONTH", NULL );
 	char *day   = fields_findv_firstof( info, level, FIELDS_CHRP,
-			"PARTDAY", "DAY", NULL );
+			"PARTDATE:DAY", "DATE:DAY", NULL );
 	if ( year )  output_itemv( outptr, "b:Year", year, 0 );
 	if ( month ) output_itemv( outptr, "b:Month", month, 0 );
 	if ( day )   output_itemv( outptr, "b:Day", day, 0 );
@@ -431,8 +463,8 @@
 static void
 output_pages( fields *info, FILE *outptr, int level )
 {
-	char *sn = fields_findv( info, LEVEL_ANY, FIELDS_CHRP, "PAGESTART" );
-	char *en = fields_findv( info, LEVEL_ANY, FIELDS_CHRP, "PAGEEND" );
+	char *sn = fields_findv( info, LEVEL_ANY, FIELDS_CHRP, "PAGES:START" );
+	char *en = fields_findv( info, LEVEL_ANY, FIELDS_CHRP, "PAGES:STOP" );
 	char *ar = fields_findv( info, LEVEL_ANY, FIELDS_CHRP, "ARTICLENUMBER" );
 	if ( sn || en )
 		output_range( outptr, "b:Pages", sn, en, level );
@@ -444,13 +476,13 @@
 output_includedin( fields *info, FILE *outptr, int type )
 {
 	if ( type==TYPE_JOURNALARTICLE ) {
-		output_titleinfo( info, outptr, "b:JournalName", 1 );
+		output_generaltitle( info, outptr, "b:JournalName", 1 );
 	} else if ( type==TYPE_ARTICLEINAPERIODICAL ) {
-		output_titleinfo( info, outptr, "b:PeriodicalTitle", 1 );
+		output_generaltitle( info, outptr, "b:PeriodicalTitle", 1 );
 	} else if ( type==TYPE_BOOKSECTION ) {
-		output_titleinfo( info, outptr, "b:ConferenceName", 1 ); /*??*/
+		output_generaltitle( info, outptr, "b:ConferenceName", 1 ); /*??*/
 	} else if ( type==TYPE_PROCEEDINGS ) {
-		output_titleinfo( info, outptr, "b:ConferenceName", 1 );
+		output_generaltitle( info, outptr, "b:ConferenceName", 1 );
 	}
 }
 
@@ -483,7 +515,7 @@
 			strcasecmp( tag, "DEGREEGRANTOR:ASIS") &
 			strcasecmp( tag, "DEGREEGRANTOR:CORP"))
 				continue;
-		output_item( info, outptr, "b:Institution", i, 0 );
+		output_item( info, outptr, "b:Institution", "", i, 0 );
 	}
 }
 
@@ -534,9 +566,9 @@
 static void
 output_comments( fields *info, FILE *outptr, int level )
 {
+	vplist_index i;
 	vplist notes;
 	char *abs;
-	int i;
 
 	vplist_init( &notes );
 
@@ -564,20 +596,26 @@
 output_citeparts( fields *info, FILE *outptr, int level, int max, int type )
 {
 	convert origin[] = {
-		{ "ADDRESS",	"b:City",	LEVEL_ANY },
-		{ "PUBLISHER",	"b:Publisher",	LEVEL_ANY },
-		{ "EDITION",	"b:Edition",	LEVEL_ANY }
+		{ "ADDRESS",	"b:City",	"", LEVEL_ANY },
+		{ "PUBLISHER",	"b:Publisher",	"", LEVEL_ANY },
+		{ "EDITION",	"b:Edition",	"", LEVEL_ANY }
 	};
 	int norigin = sizeof( origin ) / sizeof ( convert );
 	
 	convert parts[] = {
-		{ "VOLUME",          "b:Volume",  LEVEL_ANY },
-		{ "SECTION",         "b:Section", LEVEL_ANY },
-		{ "ISSUE",           "b:Issue",   LEVEL_ANY },
-		{ "NUMBER",          "b:Issue",   LEVEL_ANY },
-		{ "PUBLICLAWNUMBER", "b:Volume",  LEVEL_ANY },
-		{ "SESSION",         "b:Issue",   LEVEL_ANY },
-		{ "URL",             "b:Url",     LEVEL_ANY },
+		{ "VOLUME",          "b:Volume",  "", LEVEL_ANY },
+		{ "SECTION",         "b:Section", "", LEVEL_ANY },
+		{ "ISSUE",           "b:Issue",   "", LEVEL_ANY },
+		{ "NUMBER",          "b:Issue",   "", LEVEL_ANY },
+		{ "PUBLICLAWNUMBER", "b:Volume",  "", LEVEL_ANY },
+		{ "SESSION",         "b:Issue",   "", LEVEL_ANY },
+		{ "URL",             "b:Url",     "", LEVEL_ANY },
+		{ "JSTOR",           "b:Url",     "http://www.jstor.org/stable/", LEVEL_ANY },
+		{ "ARXIV",           "b:Url",     "http://arxiv.org/abs/",        LEVEL_ANY },
+		{ "PMID",            "b:Url",     "http://www.ncbi.nlm.nih.gov/pubmed/", LEVEL_ANY },
+		{ "PMC",             "b:Url",     "http://www.ncbi.nlm.nih.gov/pmc/articles/", LEVEL_ANY },
+		{ "DOI",             "b:Url",     "https://doi.org/", LEVEL_ANY },
+		{ "MRNUMBER",        "b:Url",     "http://www.ams.org/mathscinet-getitem?mr=", LEVEL_ANY },
 	};
 	int nparts=sizeof(parts)/sizeof(convert);
 	
@@ -589,11 +627,11 @@
 	output_list( info, outptr, parts, nparts );
 	output_pages( info, outptr, level );
 	output_names( info, outptr, level, type );
-	output_title( info, outptr, 0 );
+	output_maintitle( info, outptr, 0 );
 	output_comments( info, outptr, level );
 }
 
-void
+static int
 wordout_write( fields *info, FILE *outptr, param *p, unsigned long numrefs )
 {
 	int max = fields_maxlevel( info );
@@ -604,9 +642,11 @@
 	fprintf( outptr, "</b:Source>\n" );
 
 	fflush( outptr );
+
+	return BIBL_OK;
 }
 
-void
+static void
 wordout_writeheader( FILE *outptr, param *p )
 {
 	if ( p->utf8bom ) utf8_writebom( outptr );
@@ -616,7 +656,7 @@
 		" xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/bibliography\" >\n");
 }
 
-void
+static void
 wordout_writefooter( FILE *outptr )
 {
 	fprintf(outptr,"</b:Sources>\n");
diff --git a/bibutils/wordout.h b/bibutils/wordout.h
deleted file mode 100644
--- a/bibutils/wordout.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * wordout.h
- *
- * Copyright (c) Chris Putnam 2008-2013
- *
- * Source code released under the GPL version 2
- *
- */
-#ifndef WORDOUT_H
-#define WORDOUT_H
-
-/* format-specific options */
-#define WORDOUT_DROPKEY (2)
-
-#include <stdio.h>
-#include "bibl.h"
-#include "bibutils.h"
-
-extern void wordout_writeheader( FILE *outptr, param *p );
-extern void wordout_writefooter( FILE *outptr );
-extern void wordout_write( fields *info, FILE *outptr, param *p,
-	unsigned long numrefs );
-extern void wordout_initparams( param *p, const char *progname );
-
-
-#endif
-
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-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
@@ -11,7 +11,6 @@
 #include <string.h>
 #include "is_ws.h"
 #include "strsearch.h"
-#include "newstr.h"
 #include "xml.h"
 
 char *xml_pns = NULL;
@@ -21,8 +20,8 @@
 {
 	xml_attrib *a = (xml_attrib *) malloc( sizeof( xml_attrib ) );
 	if ( a ) {
-		list_init( &(a->attrib) );
-		list_init( &(a->value) );
+		slist_init( &(a->attrib) );
+		slist_init( &(a->value) );
 	}
 	return a;
 }
@@ -30,17 +29,17 @@
 static void
 xmlattrib_add( xml_attrib *a, char *attrib, char *value  )
 {
-	if ( attrib ) list_addc( &(a->attrib), attrib );
-	else list_addc( &(a->attrib), "" );
-	if ( value ) list_addc( &(a->value), value );
-	else list_addc( &(a->value), "" );
+	if ( attrib ) slist_addc( &(a->attrib), attrib );
+	else slist_addc( &(a->attrib), "" );
+	if ( value ) slist_addc( &(a->value), value );
+	else slist_addc( &(a->value), "" );
 }
 
 static void
 xmlattrib_free( xml_attrib *a )
 {
-	list_free( &(a->attrib) );
-	list_free( &(a->value ) );
+	slist_free( &(a->attrib) );
+	slist_free( &(a->value ) );
 }
 
 static xml *
@@ -55,11 +54,11 @@
 xml_free( xml *x )
 {
 	if ( x->tag ) {
-		newstr_free( x->tag );
+		str_free( x->tag );
 		free( x->tag );
 	}
 	if ( x->value ) {
-		newstr_free( x->value );
+		str_free( x->value );
 		free( x->value );
 	}
 	if ( x->a ) {
@@ -73,8 +72,8 @@
 void
 xml_init( xml *x )
 {
-	x->tag = newstr_new();
-	x->value = newstr_new();
+	x->tag = str_new();
+	x->value = str_new();
 	x->a = NULL;
 	x->down = NULL;
 	x->next = NULL;
@@ -117,14 +116,14 @@
 	xml_attrib *a = NULL;
 	char quote_character = '\"';
 	int inquotes = 0;
-	newstr aname, aval;
-	newstr_init( &aname );
-	newstr_init( &aval );
+	str aname, aval;
+	str_init( &aname );
+	str_init( &aval );
 	while ( *p && !xml_terminator(p,type) ) {
 		/* get attribute name */
 		while ( *p==' ' || *p=='\t' ) p++;
 		while ( *p && !strchr( "= \t", *p ) && !xml_terminator(p,type)){
-			newstr_addchar( &aname, *p );
+			str_addchar( &aname, *p );
 			p++;
 		}
 		while ( *p==' ' || *p=='\t' ) p++;
@@ -138,18 +137,18 @@
 		}
 		while ( *p && ((!xml_terminator(p,type) && !strchr("= \t", *p ))||inquotes)){
 			if ( *p==quote_character ) inquotes=0;
-			else newstr_addchar( &aval, *p );
+			else str_addchar( &aval, *p );
 			p++;
 		}
-		if ( aname.len ) {
+		if ( str_has_value( &aname ) ) {
 			if ( !a ) a = xmlattrib_new();
-			xmlattrib_add( a, aname.data, aval.data );
+			xmlattrib_add( a, str_cstr( &aname ), str_cstr( &aval ) );
 		}
-		newstr_empty( &aname );
-		newstr_empty( &aval );
+		str_empty( &aname );
+		str_empty( &aval );
 	}
-	newstr_free( &aname );
-	newstr_free( &aval );
+	str_free( &aname );
+	str_free( &aval );
 	*ap = a;
 	return p;
 }
@@ -164,30 +163,30 @@
  * 	XML_OPENCLOSE <A/>
  */
 static char *
-xml_processtag( char *p, newstr *tag, xml_attrib **attrib, int *type )
+xml_processtag( char *p, str *tag, xml_attrib **attrib, int *type )
 {
 	*attrib = NULL;
 	if ( *p=='<' ) p++;
 	if ( *p=='!' ) {
-		while ( *p && *p!='>' ) newstr_addchar( tag, *p++ );
+		while ( *p && *p!='>' ) str_addchar( tag, *p++ );
 		*type = XML_COMMENT;
 	} else if ( *p=='?' ) {
 		*type = XML_DESCRIPTOR;
 		p++; /* skip '?' */
 		while ( *p && !strchr( " \t", *p ) && !xml_terminator(p,type) )
-			newstr_addchar( tag, *p++ );
+			str_addchar( tag, *p++ );
 		if ( *p==' ' || *p=='\t' )
 			p = xml_processattrib( p, attrib, type );
 	} else if ( *p=='/' ) {
 		while ( *p && !strchr( " \t", *p ) && !xml_terminator(p,type) )
-			newstr_addchar( tag, *p++ );
+			str_addchar( tag, *p++ );
 		*type = XML_CLOSE;
 		if ( *p==' ' || *p=='\t' ) 
 			p = xml_processattrib( p, attrib, type );
 	} else {
 		*type = XML_OPEN;
 		while ( *p && !strchr( " \t", *p ) && !xml_terminator(p,type) )
-			newstr_addchar( tag, *p++ );
+			str_addchar( tag, *p++ );
 		if ( *p==' ' || *p=='\t' ) 
 			p = xml_processattrib( p, attrib, type );
 	}
@@ -210,28 +209,28 @@
 char *
 xml_tree( char *p, xml *onode )
 {
-	newstr tag;
+	str tag;
 	xml_attrib *attrib;
 	int type, is_style = 0;
 
-	newstr_init( &tag );
+	str_init( &tag );
 
 	while ( *p ) {
 		/* retain white space for <style> tags in endnote xml */
-		if ( onode->tag && onode->tag->data && 
-			!strcasecmp(onode->tag->data,"style") ) is_style=1;
+		if ( onode->tag && str_cstr( onode->tag ) &&
+			!strcasecmp( str_cstr( onode->tag ),"style") ) is_style=1;
 		while ( *p && *p!='<' ) {
 			if ( onode->value->len>0 || is_style || !is_ws( *p ) )
-				newstr_addchar( onode->value, *p );
+				str_addchar( onode->value, *p );
 			p++;
 		}
 		if ( *p=='<' ) {
-			newstr_empty( &tag );
+			str_empty( &tag );
 			p = xml_processtag( p, &tag, &attrib, &type );
 			if ( type==XML_OPEN || type==XML_OPENCLOSE ||
 			     type==XML_DESCRIPTOR ) {
 				xml *nnode = xml_new();
-				newstr_newstrcpy( nnode->tag, &tag );
+				str_strcpy( nnode->tag, &tag );
 				nnode->a = attrib;
 				xml_appendnode( onode, nnode );
 				if ( type==XML_OPEN )
@@ -243,7 +242,7 @@
 		}
 	}
 out:
-	newstr_free( &tag );
+	str_free( &tag );
 	return p;
 }
 
@@ -253,13 +252,13 @@
 	int i,j;
 	if ( !x ) return;
 	for ( i=0; i<n; ++i ) printf( "    " );
-	printf("n=%d tag='%s' value='%s'\n", n, x->tag->data, x->value->data );
+	printf("n=%d tag='%s' value='%s'\n", n, str_cstr( x->tag ), str_cstr( x->value ) );
 	if ( x->a ) {
 		for ( j=0; j<x->a->value.n; ++j ) {
 			for ( i=0; i<n; ++i ) printf( "    " );
 			printf("    attrib='%s' value='%s'\n",
-				(x->a)->attrib.str[j].data,
-				(x->a)->value.str[j].data );
+				slist_cstr( &(x->a)->attrib, j ),
+				slist_cstr( &(x->a)->attrib, j ) );
 		}
 	}
 	if ( x->down ) xml_draw( x->down, n+1 );
@@ -269,85 +268,95 @@
 char *
 xml_findstart( char *buffer, char *tag )
 {
-	newstr starttag;
+	str starttag;
 	char *p;
 
-	newstr_init( &starttag );
-	newstr_addchar( &starttag, '<' );
-	newstr_strcat( &starttag, tag );
-	newstr_addchar( &starttag, ' ' );
-	p = strsearch( buffer, starttag.data );
+	str_init( &starttag );
+	str_addchar( &starttag, '<' );
+	str_strcatc( &starttag, tag );
+	str_addchar( &starttag, ' ' );
+	p = strsearch( buffer, str_cstr( &starttag ) );
 
 	if ( !p ) {
 		starttag.data[ starttag.len-1 ] = '>';
-		p = strsearch( buffer, starttag.data );
+		p = strsearch( buffer, str_cstr( &starttag ) );
 	}
 
-	newstr_free( &starttag );
+	str_free( &starttag );
 	return p;
 }
 
 char *
 xml_findend( char *buffer, char *tag )
 {
-	newstr endtag;
+	str endtag;
 	char *p;
 
-	newstr_init( &endtag );
-	newstr_strcpy( &endtag, "</" );
+	str_init( &endtag );
+	str_strcpyc( &endtag, "</" );
 	if ( xml_pns ) {
-		newstr_strcat( &endtag, xml_pns );
-		newstr_addchar( &endtag, ':' );
+		str_strcatc( &endtag, xml_pns );
+		str_addchar( &endtag, ':' );
 	}
-	newstr_strcat( &endtag, tag );
-	newstr_addchar( &endtag, '>' );
+	str_strcatc( &endtag, tag );
+	str_addchar( &endtag, '>' );
 
-	p = strsearch( buffer, endtag.data );
+	p = strsearch( buffer, str_cstr( &endtag ) );
 
 	if ( p && *p ) {
 		if ( *p ) p++;  /* skip <random_tag></end> combo */
 		while ( *p && *(p-1)!='>' ) p++;
 	}
 
-	newstr_free( &endtag );
+	str_free( &endtag );
 	return p;
 }
 
-int
-xml_tagexact( xml *node, char *s )
+static int
+xml_tagexact_simple( xml* node, char *s )
 {
-	newstr tag;
+	if ( node->tag->len!=strlen( s ) ) return 0;
+	if ( strcasecmp( str_cstr( node->tag ), s ) ) return 0;
+	return 1;
+}
+static int
+xml_tagexact_pns( xml* node, char *s )
+{
 	int found = 0;
-	if ( xml_pns ) {
-		newstr_init( &tag );
-		newstr_strcpy( &tag, xml_pns );
-		newstr_addchar( &tag, ':' );
-		newstr_strcat( &tag, s );
-		if ( node->tag->len==tag.len &&
-				!strcasecmp( node->tag->data, tag.data ) )
-			found = 1;
-		newstr_free( &tag );
-	} else {
-		if ( node->tag->len==strlen( s ) && 
-				!strcasecmp( node->tag->data, s ) )
-			found = 1;
-	}
+	str tag;
+
+	str_init( &tag );
+	str_strcpyc( &tag, xml_pns );
+	str_addchar( &tag, ':' );
+	str_strcatc( &tag, s );
+	if ( node->tag->len==tag.len &&
+			!strcasecmp( str_cstr( node->tag ), str_cstr( &tag ) ) )
+		found = 1;
+	str_free( &tag );
+
 	return found;
 }
+int
+xml_tagexact( xml *node, char *s )
+{
+	if ( xml_pns ) return xml_tagexact_pns( node, s );
+	else           return xml_tagexact_simple( node, s );
+}
 
 int
 xml_hasattrib( xml *node, char *attrib, char *value )
 {
 	xml_attrib *na = node->a;
+	char *a, *v;
 	int i;
 
 	if ( na ) {
 
 		for ( i=0; i<na->attrib.n; ++i ) {
-			if ( !na->attrib.str[i].data || !na->value.str[i].data )
-				continue;
-			if ( !strcasecmp( na->attrib.str[i].data, attrib ) &&
-			     !strcasecmp( na->value.str[i].data, value ) )
+			a = slist_cstr( &(na->attrib), i );
+			v = slist_cstr( &(na->value),  i );
+			if ( !a || !v ) continue;
+			if ( !strcasecmp( a, attrib ) && !strcasecmp( v, value ) )
 				return 1;
 		}
 
@@ -363,38 +372,43 @@
 	return xml_hasattrib( node, attrib, value );
 }
 
-newstr *
+str *
 xml_getattrib( xml *node, char *attrib )
 {
-	newstr *ns = NULL;
 	xml_attrib *na = node->a;
-	int i, nattrib;
+	str *ns = NULL;
+	int i;
+
 	if ( na ) {
-		nattrib = na->attrib.n;
-		for ( i=0; i<nattrib; ++i )
-			if ( !strcasecmp( na->attrib.str[i].data, attrib ) )
-				ns = &(na->value.str[i]);
+		for ( i=0; i<na->attrib.n; ++i )
+			if ( !strcasecmp( slist_cstr( &(na->attrib), i ), attrib ) )
+				ns = slist_str( &(na->value), i );
 	}
 	return ns;
 }
 
 int
-xml_hasdata( xml *node )
+xml_hasvalue( xml *node )
 {
-	if ( node && node->value && node->value->data ) return 1;
+	if ( node && node->value && str_cstr( node->value ) ) return 1;
 	return 0;
 }
 
 char *
-xml_data( xml *node )
+xml_tag( xml *node )
 {
-	return node->value->data;
+	return str_cstr( node->tag );
 }
 
+char *
+xml_value( xml *node )
+{
+	return str_cstr( node->value );
+}
+
 int
-xml_tagwithdata( xml *node, char *tag )
+xml_tagwithvalue( xml *node, char *tag )
 {
-	if ( !xml_hasdata( node ) ) return 0;
+	if ( !xml_hasvalue( node ) ) return 0;
 	return xml_tagexact( node, tag );
 }
-
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-2013
+ * Copyright (c) Chris Putnam 2004-2017
  *
  * Source code released under the GPL version 2
  *
@@ -9,36 +9,37 @@
 #ifndef XML_H
 #define XML_H
 
-#include "list.h"
-#include "newstr.h"
+#include "slist.h"
+#include "str.h"
 
 typedef struct xml_attrib {
-	list attrib;
-	list value;
+	slist attrib;
+	slist value;
 } xml_attrib;
 
 typedef struct xml {
-	newstr *tag;
-	newstr *value;
+	str *tag;
+	str *value;
 	xml_attrib *a;
 	struct xml *down;
 	struct xml *next;
 } xml;
 
-extern void xml_init( xml *x );
-extern newstr * xml_getattrib( xml *node, char *attrib );
-extern char * xml_findstart( char *buffer, char *tag );
-extern char * xml_findend( char *buffer, char *tag );
-extern int xml_tagexact( xml *node, char *s );
-extern int xml_tag_attrib( xml *node, char *s, char *attrib, char *value );
-extern int xml_hasattrib( xml *node, char *attrib, char *value );
-extern void xml_free( xml *x );
-extern char * xml_tree( char *p, xml *onode );
-extern int xml_hasdata( xml *node );
-extern char * xml_data( xml *node );
-extern int xml_tagwithdata( xml *node, char *tag );
+void     xml_init        ( xml *x );
+void     xml_free        ( xml *x );
+str *    xml_getattrib   ( xml *node, char *attrib );
+char *   xml_findstart   ( char *buffer, char *tag );
+char *   xml_findend     ( char *buffer, char *tag );
+int      xml_tagexact    ( xml *node, char *s );
+int      xml_tag_attrib  ( xml *node, char *s, char *attrib, char *value );
+int      xml_hasattrib   ( xml *node, char *attrib, char *value );
+char *   xml_tree        ( char *p, xml *onode );
+int      xml_hasvalue    ( xml *node );
+char *   xml_value       ( xml *node );
+char *   xml_tag         ( xml *node );
+int      xml_tagwithvalue( xml *node, char *tag );
 
-extern char *xml_pns; /* global Namespace */
+char *   xml_pns; /* global Namespace */
 
 #endif
 
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-2013
+ * Copyright (c) Chris Putnam 2007-2017
  *
  * Source code released under the GPL version 2
  *
@@ -10,29 +10,31 @@
 #include <stdlib.h>
 #include <string.h>
 #include "charsets.h"
-#include "newstr.h"
-#include "newstr_conv.h"
+#include "str.h"
+#include "str_conv.h"
 #include "xml.h"
 #include "xml_encoding.h"
 
 static int
 xml_getencodingr( xml *node )
 {
-	newstr *s;
 	int n = CHARSET_UNKNOWN, m;
+	str *s;
+	char *t;
+
 	if ( xml_tagexact( node, "xml" ) ) {
 		s = xml_getattrib( node, "encoding" );
-		if ( s && s->data ) {
-			if ( !strcasecmp( s->data, "UTF-8" ) )
+		if ( str_has_value( s ) ) {
+			t = str_cstr( s );
+			if ( !strcasecmp( t, "UTF-8" ) )
 				n = CHARSET_UNICODE;
-			else if ( !strcasecmp( s->data, "UTF8" ) )
+			else if ( !strcasecmp( t, "UTF8" ) )
 				n = CHARSET_UNICODE;
-			else if ( !strcasecmp( s->data, "GB18030" ) )
+			else if ( !strcasecmp( t, "GB18030" ) )
 				n = CHARSET_GB18030;
-			else n = charset_find( s->data );
+			else n = charset_find( t );
 			if ( n==CHARSET_UNKNOWN ) {
-				fprintf( stderr, "Warning: did not recognize "
-					"encoding '%s'\n", s->data );
+				fprintf( stderr, "Warning: did not recognize encoding '%s'\n", t );
 			}
 		}
 	}
@@ -44,29 +46,31 @@
 		m = xml_getencodingr( node->next );
 		if ( m!=CHARSET_UNKNOWN ) n = m;
 	}
+
 	return n;
 }
 
 int
-xml_getencoding( newstr *s )
+xml_getencoding( str *s )
 {
-	newstr descriptor;
-	xml descriptxml;
 	int file_charset = CHARSET_UNKNOWN;
+	str descriptor;
+	xml descriptxml;
 	char *p, *q;
-	p = strstr( s->data, "<?xml" );
-	if ( !p ) p = strstr( s->data, "<?XML" );
+
+	p = strstr( str_cstr( s ), "<?xml" );
+	if ( !p ) p = strstr( str_cstr( s ), "<?XML" );
 	if ( p ) {
 		q = strstr( p, "?>" );
 		if ( q ) {
-			newstr_init( &descriptor );
-			newstr_segcpy( &descriptor, p, q+2 );
+			str_init( &descriptor );
+			str_segcpy( &descriptor, p, q+2 );
 			xml_init( &descriptxml );
-			xml_tree( descriptor.data, &descriptxml );
+			xml_tree( str_cstr( &descriptor ), &descriptxml );
 			file_charset = xml_getencodingr( &descriptxml );
 			xml_free( &descriptxml );
-			newstr_free( &descriptor );
-			newstr_segdel( s, p, q+2 );
+			str_free( &descriptor );
+			str_segdel( s, p, q+2 );
 		}
 	}
 	return file_charset;
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-2013
+ * Copyright (c) Chris Putnam 2007-2017
  *
  * Source code released under the GPL version 2
  *
@@ -9,6 +9,6 @@
 #ifndef XML_GETENCODING_H
 #define XML_GETENCODING_H
 
-extern int xml_getencoding( newstr *s );
+extern 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,6 +1,6 @@
 name:               hs-bibutils
-version:            5.5
-homepage:           http://istitutocolli.org/repos/hs-bibutils/
+version:            6.2
+homepage:           https://github.com/wilx/hs-bibutils
 
 synopsis:           Haskell bindings to bibutils, the bibliography
                     conversion utilities.
@@ -9,15 +9,12 @@
                     library that interconverts between various
                     bibliography formats using a common MODS-format
                     XML intermediate.
-		    .
+                    .
                     hs-bibutils is available under the GNU GPL license. See
                     the LICENSE file for details.
                     .
                     For more details about bibutils, please check:
-		    <http://sourceforge.net/p/bibutils/home/Bibutils/>.
-                    .
-                    The original API documentation is available here:
-                    <http://www.scripps.edu/~cdputnam/software/bibutils/library_specs.html>.
+                    <http://sourceforge.net/p/bibutils/home/Bibutils/>.
                     .
                     The package release number refers to the release number of
                     the included bibutils library.
@@ -26,78 +23,78 @@
 license:            GPL
 license-file:       LICENSE
 author:             Andrea Rossato
-maintainer:         andrea.rossato@unitn.it
-cabal-version:      >= 1.4
+maintainer:         Václav Haisman <vhaisman@gmail.com>
+cabal-version:      >= 1.23
 build-type:         Simple
 extra-source-files:
-        bibutils/adsout.c bibutils/adsout.h bibutils/adsout_journals.c
-        bibutils/bibcore.c bibutils/bibl.c bibutils/bibl.h
-        bibutils/biblatexin.c bibutils/biblatexin.h
-        bibutils/bibtexin.c bibutils/bibtexin.h bibutils/bibtexout.c
-        bibutils/bibtexout.h bibutils/bibtextypes.c
-        bibutils/bibutils.c bibutils/bibutils.h bibutils/bltypes.c
-        bibutils/charsets.c bibutils/charsets.h bibutils/copacin.c
-        bibutils/copacin.h bibutils/copactypes.c bibutils/doi.c
-        bibutils/doi.h bibutils/ebiin.c bibutils/ebiin.h
-        bibutils/endin.c bibutils/endin.h bibutils/endout.c
-        bibutils/endout.h bibutils/endtypes.c bibutils/endxmlin.c
-        bibutils/endxmlin.h bibutils/entities.c bibutils/entities.h
+        bibutils/adsout.c bibutils/adsout_journals.c bibutils/bibcore.c
+        bibutils/bibformats.h bibutils/biblatexin.c bibutils/bibl.c
+        bibutils/bibl.h bibutils/bibtexin.c bibutils/bibtexout.c
+        bibutils/bibtextypes.c bibutils/bibutils.c bibutils/bibutils.h
+        bibutils/bltypes.c bibutils/charsets.c bibutils/charsets.h
+        bibutils/copacin.c bibutils/copactypes.c bibutils/ebiin.c
+        bibutils/endin.c bibutils/endout.c bibutils/endtypes.c
+        bibutils/endxmlin.c bibutils/entities.c bibutils/entities.h
         bibutils/fields.c bibutils/fields.h bibutils/gb18030.c
-        bibutils/gb18030.h bibutils/gb18030_enumeration.c
-        bibutils/intlist.c bibutils/intlist.h bibutils/is_ws.c
-        bibutils/is_ws.h bibutils/isiin.c bibutils/isiin.h
-        bibutils/isiout.c bibutils/isiout.h bibutils/isitypes.c
+        bibutils/gb18030_enumeration.c bibutils/gb18030.h bibutils/generic.c
+        bibutils/generic.h bibutils/intlist.c bibutils/intlist.h
+        bibutils/isiin.c bibutils/isiout.c bibutils/isitypes.c
         bibutils/iso639_1.c bibutils/iso639_1.h bibutils/iso639_2.c
         bibutils/iso639_2.h bibutils/iso639_3.c bibutils/iso639_3.h
-        bibutils/latex.c bibutils/latex.h bibutils/list.c
-        bibutils/list.h bibutils/marc.c bibutils/marc.h
-        bibutils/medin.c bibutils/medin.h bibutils/modsin.c
-        bibutils/modsin.h bibutils/modsout.c bibutils/modsout.h
-        bibutils/modstypes.c bibutils/modstypes.h bibutils/name.c
-        bibutils/name.h bibutils/newstr.c bibutils/newstr.h
-        bibutils/newstr_conv.c bibutils/newstr_conv.h
-        bibutils/reftypes.c bibutils/reftypes.h bibutils/risin.c
-        bibutils/risin.h bibutils/risout.c bibutils/risout.h
+        bibutils/is_ws.c bibutils/is_ws.h bibutils/latex.c bibutils/latex.h
+        bibutils/marc.c bibutils/marc.h bibutils/medin.c bibutils/modsin.c
+        bibutils/modsout.c bibutils/modstypes.c bibutils/modstypes.h
+        bibutils/name.c bibutils/name.h bibutils/nbibin.c
+        bibutils/nbibtypes.c bibutils/notes.c bibutils/notes.h
+        bibutils/pages.c bibutils/pages.h bibutils/reftypes.c
+        bibutils/reftypes.h bibutils/risin.c bibutils/risout.c
         bibutils/ristypes.c bibutils/serialno.c bibutils/serialno.h
-        bibutils/strsearch.c bibutils/strsearch.h bibutils/title.c
-        bibutils/title.h bibutils/unicode.c bibutils/unicode.h
-        bibutils/utf8.c bibutils/utf8.h bibutils/vplist.c
-        bibutils/vplist.h bibutils/wordin.c bibutils/wordin.h
-        bibutils/wordout.c bibutils/wordout.h bibutils/xml.c
-        bibutils/xml.h bibutils/xml_encoding.c bibutils/xml_encoding.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
 
 library
+    default-language: Haskell2010
     exposed-modules:  Text.Bibutils
     other-modules:
     ghc-options:      -funbox-strict-fields -Wall
-    ghc-prof-options: -prof -auto-all
     hs-source-dirs:   src
-    extensions:       ForeignFunctionInterface
+    default-extensions:       ForeignFunctionInterface
     include-dirs:     bibutils
     includes: bibutils.h
     c-sources:
         cbits/stub.c
-        bibutils/adsout.c bibutils/adsout_journals.c
-        bibutils/bibcore.c bibutils/bibl.c bibutils/biblatexin.c
-        bibutils/bibtexin.c bibutils/bibtexout.c
-        bibutils/bibtextypes.c bibutils/bibutils.c bibutils/bltypes.c
-        bibutils/charsets.c bibutils/copacin.c bibutils/copactypes.c
-        bibutils/doi.c bibutils/ebiin.c bibutils/endin.c
+        bibutils/adsout.c bibutils/adsout_journals.c bibutils/bibcore.c
+        bibutils/biblatexin.c bibutils/bibl.c bibutils/bibtexin.c
+        bibutils/bibtexout.c bibutils/bibtextypes.c bibutils/bibutils.c
+        bibutils/bltypes.c bibutils/charsets.c bibutils/copacin.c
+        bibutils/copactypes.c bibutils/ebiin.c bibutils/endin.c
         bibutils/endout.c bibutils/endtypes.c bibutils/endxmlin.c
         bibutils/entities.c bibutils/fields.c bibutils/gb18030.c
-        bibutils/intlist.c bibutils/is_ws.c bibutils/isiin.c
-        bibutils/isiout.c bibutils/isitypes.c bibutils/iso639_1.c
-        bibutils/iso639_2.c bibutils/iso639_3.c bibutils/latex.c
-        bibutils/list.c bibutils/marc.c bibutils/medin.c
+        bibutils/generic.c bibutils/intlist.c
+        bibutils/isiin.c bibutils/isiout.c bibutils/isitypes.c
+        bibutils/iso639_1.c bibutils/iso639_2.c bibutils/iso639_3.c
+        bibutils/is_ws.c bibutils/latex.c bibutils/marc.c bibutils/medin.c
         bibutils/modsin.c bibutils/modsout.c bibutils/modstypes.c
-        bibutils/name.c bibutils/newstr.c bibutils/newstr_conv.c
-        bibutils/reftypes.c bibutils/risin.c bibutils/risout.c
-        bibutils/ristypes.c bibutils/serialno.c bibutils/strsearch.c
-        bibutils/title.c bibutils/unicode.c bibutils/utf8.c
-        bibutils/vplist.c bibutils/wordin.c bibutils/wordout.c
-        bibutils/xml.c bibutils/xml_encoding.c
+        bibutils/name.c bibutils/nbibin.c bibutils/nbibtypes.c
+        bibutils/notes.c 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
 
     if impl(ghc >= 6.10)
        build-depends: base >= 4, syb
     else
        build-depends: base >= 3 && < 4
+
+source-repository head
+    type:     git
+    location: https://github.com/wilx/hs-bibutils
diff --git a/src/Text/Bibutils.hsc b/src/Text/Bibutils.hsc
--- a/src/Text/Bibutils.hsc
+++ b/src/Text/Bibutils.hsc
@@ -5,7 +5,7 @@
 -- Copyright   :  (C) 2008 Andrea Rossato
 -- License     :  BSD3
 --
--- Maintainer  :  andrea.rossato@unitn.it
+-- Maintainer  :  vhaisman@gmail.com
 -- Stability   :  provisional
 -- Portability :  portable
 --
@@ -98,7 +98,6 @@
     , bibout_uppercase
     , bibout_strictkey
     , modsout_dropkey
-    , wordout_dropkey
 
     -- * Charsets
     , Charset
@@ -403,19 +402,16 @@
 
 newtype FormatOpt = FormatOpt { unFormatOpt :: CInt }
 
-#include "bibtexout.h"
-#include "modsout.h"
-#include "wordout.h"
+#include "bibutils.h"
 
 #{enum FormatOpt, FormatOpt
- , bibout_finalcomma = BIBOUT_FINALCOMMA
- , bibout_singledash = BIBOUT_SINGLEDASH
- , bibout_whitespace = BIBOUT_WHITESPACE
- , bibout_brackets   = BIBOUT_BRACKETS
- , bibout_uppercase  = BIBOUT_UPPERCASE
- , bibout_strictkey  = BIBOUT_STRICTKEY
- , modsout_dropkey   = MODSOUT_DROPKEY
- , wordout_dropkey   = WORDOUT_DROPKEY
+ , bibout_finalcomma = BIBL_FORMAT_BIBOUT_FINALCOMMA
+ , bibout_singledash = BIBL_FORMAT_BIBOUT_SINGLEDASH
+ , bibout_whitespace = BIBL_FORMAT_BIBOUT_WHITESPACE
+ , bibout_brackets   = BIBL_FORMAT_BIBOUT_BRACKETS
+ , bibout_uppercase  = BIBL_FORMAT_BIBOUT_UPPERCASE
+ , bibout_strictkey  = BIBL_FORMAT_BIBOUT_STRICTKEY
+ , modsout_dropkey   = BIBL_FORMAT_MODSOUT_DROPKEY
  }
 
 newtype Status = Status { status :: CInt }
