packages feed

hs-bibutils-0.2: bibutils/bibutils.c

/*
 * bibutils.c
 *
 * Copyright (c) Chris Putnam 2005-2009
 *
 * Source code released under the GPL
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include "bibutils.h"

/* internal includes */
#include "reftypes.h"
#include "bibtexin.h"
#include "biblatexin.h"
#include "bibtexout.h"
#include "copacin.h"
#include "endin.h"
#include "endxmlin.h"
#include "endout.h"
#include "isiin.h"
#include "isiout.h"
#include "medin.h"
#include "modsin.h"
#include "risin.h"
#include "wordin.h"
#include "risout.h"
#include "modsout.h"
#include "wordout.h"
#include "adsout.h"
#include "newstr_conv.h"
#include "is_ws.h"

#include "ebiin.h"

typedef struct convert_rules {
	int  (*readf)(FILE*,char*,int,int*,newstr*,newstr*,int*);
	int  (*processf)(fields*,char*,char*,long);
	void (*cleanf)(bibl*,param*);
	int  (*typef) (fields*,char*,int,param*,variants*,int);
	void (*convertf)(fields*,fields*,int,param*,variants*,int);
	void (*headerf)(FILE*,param*);
	void (*footerf)(FILE*);
	void (*writef)(fields*,FILE*,param*,unsigned long);
	variants *all;
	int  nall;
} convert_rules;

void
bibl_initparams( param *p, int readmode, int writemode, char *progname )
{
	p->readformat       = readmode;
	p->writeformat      = writemode;
	p->format_opts      = 0;
	p->charsetin        = BIBL_CHARSET_DEFAULT;
	p->charsetin_src    = BIBL_SRC_DEFAULT;
	p->charsetout       = BIBL_CHARSET_DEFAULT;
	p->charsetout_src   = BIBL_SRC_DEFAULT;
	p->latexin          = 0;
	p->latexout         = 0;
	p->utf8in           = 0;
	p->utf8out          = 0;
	p->utf8bom          = 0;
	p->xmlout           = 0;
	p->nosplittitle     = 0;
	p->verbose          = 0;
	p->addcount         = 0;
	p->singlerefperfile = 0;
	p->output_raw       = 0;	/* keep MODS tags for output filter */

	list_init( &(p->asis) );
	list_init( &(p->corps) );

	if ( !progname ) p->progname = NULL;
	else p->progname = strdup( progname );

	if ( readmode == BIBL_BIBTEXIN || readmode == BIBL_BIBLATEXIN ) {
		p->latexin = 1;
	} else if ( readmode == BIBL_MODSIN ) {
		p->xmlin = 1;
		p->utf8in = 1;
		p->output_raw = BIBL_RAW_WITHMAKEREFID |
				BIBL_RAW_WITHCHARCONVERT;
		p->charsetin = BIBL_CHARSET_UNICODE;
	} else if ( readmode == BIBL_WORDIN ) {
		p->xmlin = 1;
		p->utf8in = 1;
		p->output_raw = BIBL_RAW_WITHMAKEREFID |
				BIBL_RAW_WITHCHARCONVERT;
		p->charsetin = BIBL_CHARSET_UNICODE;
	} else if ( readmode == BIBL_MEDLINEIN ) {
		/* default medline to UTF8 Unicode */
		p->charsetin = BIBL_CHARSET_UNICODE;
		p->utf8in = 1;
		p->xmlin = 1;
		p->output_raw = BIBL_RAW_WITHMAKEREFID |
				BIBL_RAW_WITHCHARCONVERT;
	} else if ( readmode == BIBL_EBIIN ) {
		/* default medline to UTF8 Unicode */
		p->charsetin = BIBL_CHARSET_UNICODE;
		p->utf8in = 1;
		p->xmlin = 1;
		p->output_raw = BIBL_RAW_WITHMAKEREFID |
				BIBL_RAW_WITHCHARCONVERT;
	} else if ( readmode == BIBL_ENDNOTEXMLIN ) {
		p->xmlin = 1;
		p->utf8in = 1;
	}

	if ( writemode == BIBL_BIBTEXOUT ) {
		p->latexout = 1;
	} else if ( writemode == BIBL_MODSOUT ) {
		/* default to UTF8 Unicode with BOM written */
		p->charsetout = BIBL_CHARSET_UNICODE;
		p->utf8out = 1;
		p->utf8bom = 1;
		p->xmlout = 1;
	} else if ( writemode == BIBL_WORD2007OUT ) {
		if ( !p->utf8out ) p->xmlout = 3;
		else p->xmlout = 1;
		p->charsetout = BIBL_CHARSET_UNICODE;
	} else p->xmlout = 0;

}

void
bibl_freeparams( param *p )
{
	list_free( &(p->asis) );
	list_free( &(p->corps) );
	if ( p->progname ) free( p->progname );
}

static void
bibl_readlist( list *pl, char *progname, char *filename )
{
	if ( !list_fill( pl, filename ) ) {
		fprintf( stderr, "%s: warning problems reading '%s' "
			"obtained %d elements\n", progname, filename,
			pl->n );
	}
}

void
bibl_readasis( param *p, char *filename )
{
	bibl_readlist( &(p->asis), p->progname, filename );
}

void
bibl_readcorps( param *p, char *filename )
{
	bibl_readlist( &(p->corps), p->progname, filename );
}

static void
bibl_addtolist( list *pl, char *entry )
{
	list_add( pl, entry );
}

void
bibl_addtoasis( param *p, char *entry )
{
	bibl_addtolist( &(p->asis), entry );
}

void
bibl_addtocorps( param *p, char *entry )
{
	bibl_addtolist( &(p->corps), entry );
}

static void
bibl_duplicateparams( param *np, param *op )
{
	list_init( &(np->asis) );
	list_init( &(np->corps) );
	list_copy( &(np->asis), &(op->asis ) );
	list_copy( &(np->corps), &(op->corps ) );
	
	if ( !op->progname ) np->progname = NULL;
	else np->progname = strdup( op->progname );

	np->readformat = op->readformat;
	np->writeformat = op->writeformat;

	np->charsetin = op->charsetin;
	np->charsetin_src = op->charsetin_src;
	np->utf8in = op->utf8in;
	np->latexin = op->latexin;
	np->xmlin = op->xmlin;

	np->charsetout = op->charsetout;
	np->charsetout_src = op->charsetout_src;
	np->utf8out = op->utf8out;
	np->utf8bom = op->utf8bom;
	np->latexout = op->latexout;
	np->xmlout = op->xmlout;
	np->nosplittitle = op->nosplittitle;

	np->verbose = op->verbose;
	np->format_opts = op->format_opts;
	np->addcount = op->addcount;
	np->output_raw = op->output_raw;
	np->singlerefperfile = op->singlerefperfile;
}

void
bibl_reporterr( int err )
{
	fprintf( stderr, "Bibutils: " );
	switch( err ) {
		case BIBL_OK:
			fprintf( stderr, "No error." ); break;
		case BIBL_ERR_BADINPUT:
			fprintf( stderr, "Bad input." ); break;
		case BIBL_ERR_MEMERR:
			fprintf( stderr, "Memory error." ); break;
		case BIBL_ERR_CANTOPEN:
			fprintf( stderr, "Can't open." ); break;
		default:
			fprintf( stderr, "Cannot identify error code."); break;
	}
	fprintf( stderr, "\n" );
}

static int
bibl_illegalinmode( int mode )
{
	if ( mode < BIBL_FIRSTIN || mode > BIBL_LASTIN ) return 1;
	else return 0;
}

static int
bibl_illegaloutmode( int mode )
{
	if ( mode < BIBL_FIRSTOUT || mode > BIBL_LASTOUT ) return 1;
	else return 0;
}

void
bibl_verbose2( fields *info, char *filename, long nrefs )
{
	int i;
	fprintf( stderr, "======== %s %ld : converted\n", filename, nrefs );
	for ( i=0; i<info->nfields; ++i ) {
		fprintf( stderr, "'%s'='%s' level=%d\n", info->tag[i].data,
				info->data[i].data , info->level[i]);
	}
	fprintf( stderr, "\n" );
	fflush( stderr );
}

void
bibl_verbose1( fields *info, fields *orig, char *filename, long nrefs )
{
	int i;
	fprintf( stderr, "======== %s %ld : processed\n", filename, nrefs );
	for ( i=0; i<orig->nfields; ++i ) {
		fprintf( stderr, "'%s'='%s' level=%d\n", orig->tag[i].data,
				orig->data[i].data , orig->level[i]);
	}
	if ( info ) bibl_verbose2( info, filename, nrefs );
}

void
bibl_verbose0( bibl *bin )
{
	int i;
	for ( i=0; i<bin->nrefs; ++i )
		bibl_verbose2( bin->ref[i], "", i+1 );
}

static void
process_alwaysadd( fields *info, int reftype, convert_rules *r )
{
        char tag[512], data[512], *p, *q;
        int i, process, level;
        for ( i=0; i<r->all[reftype].ntags; ++i ) {
                process = ((r->all[reftype]).tags[i]).processingtype;
                if ( process!=ALWAYS ) continue;
                level   = ((r->all[reftype]).tags[i]).level;
                p = ((r->all[reftype]).tags[i]).newstr;
                q = tag;
                while ( p && *p && *p!='|' ) {
                        *q = *p;
                        q++;
                        p++;
                }
                *q = '\0';
                q = data;
                if ( p && *p=='|' ) p++;
                while ( p && *p ) {
                        *q = *p;
                        q++;
                        p++;
                }
                *q = '\0';
                fields_add( info, tag, data, level );
        }
}

static int
read_ref( FILE *fp, bibl *bin, char *filename, convert_rules *r, param *p )
{
	newstr reference, line;
	fields *ref;
	char buf[256]="";
	int nrefs = 0, bufpos = 0, fcharset;/* = CHARSET_UNKNOWN;*/
	newstr_init( &reference );
	newstr_init( &line );
	while ( r->readf( fp, buf, sizeof(buf), &bufpos, &line, &reference, &fcharset ) ) {
		if ( reference.len==0 ) continue;
		ref = fields_new();
		if ( !ref ) return BIBL_ERR_MEMERR;
		if ( r->processf( ref, reference.data, filename, nrefs+1 )){
			bibl_addref( bin, ref );
		} else {
			fields_free( ref );
			free( ref );
		}
		newstr_empty( &reference );
		if ( fcharset!=CHARSET_UNKNOWN ) {
			/* charset from file takes priority over default, but
			 * not user-specified */
			if ( p->charsetin_src!=BIBL_SRC_USER ) {
				p->charsetin_src = BIBL_SRC_FILE;
				p->charsetin = fcharset;
				if ( fcharset!=CHARSET_UNICODE ) p->utf8in = 0;
			}
		}
	}
	if ( p->charsetin==CHARSET_UNICODE ) p->utf8in = 1;
	newstr_free( &line );
	newstr_free( &reference );
	return BIBL_OK;
}

static void
bibl_fixcharsets( bibl *b, param *p )
{
	fields *ref;
	long i, j;
	int swap = 0;
	int latexout = p->latexout;
	int latexin  = p->latexin;
	for ( i=0; i<b->nrefs; ++i ) {
		ref = b->ref[i];
		for ( j=0; j<ref->nfields; ++j ) {
			if ( latexin || latexout ) {
				/* don't texify/detexify URL's and the like */
				if ( !strcasecmp( ref->tag[j].data, "DOI" ) ||
				     !strcasecmp( ref->tag[j].data, "URL" ) ||
				     !strcasecmp( ref->tag[j].data, "REFNUM" )){
					latexin  = 0;
					latexout = 0;
					swap = 1;
				}
			}
			newstr_convert( &(ref->data[j]),
				p->charsetin,  latexin,  p->utf8in,  
				p->xmlin,
				p->charsetout, latexout, p->utf8out, 
				p->xmlout );
			if ( swap ) {
				swap = 0;
				latexin  = p->latexin;
				latexout = p->latexout;
			}
		}
	}
}

static int
build_refnum( fields *info, long nrefs )
{
	newstr refnum;
	char *p, num[512];
	int y, a;
	newstr_init( &refnum );
	y = fields_find( info, "YEAR", 0 );
	if ( y==-1 ) y = fields_find( info, "YEAR", -1 );
	if ( y==-1 ) y = fields_find( info, "PARTYEAR", -1 );
	a = fields_find( info, "AUTHOR", 0 );
	if ( a==-1 ) a = fields_find( info, "AUTHOR", -1 );
	if ( a==-1 ) a = fields_find( info, "AUTHOR:CORP", -1 );
	if ( a==-1 ) a = fields_find( info, "AUTHOR:ASIS", -1 );
	if ( a!=-1 && y!=-1 ) {
		p = info->data[a].data;
		while ( p && *p && *p!='|' )
			newstr_addchar( &refnum, *p++ );
		p = info->data[y].data;
		while ( p && *p && *p!=' ' && *p!='\t' )
			newstr_addchar( &refnum, *p++ );
	} else {
		sprintf( num, "%ld", nrefs );
		newstr_strcpy( &refnum, "ref" );
		newstr_strcat( &refnum, num );
	}
	fields_add( info, "REFNUM", refnum.data, 0 );
	newstr_free( &refnum );
	return fields_find( info, "REFNUM", 0 );
}

static void
bibl_checkrefid( bibl *b, param *p )
{
	fields *ref;
	long i;
	char buf[512];
	int n;
	for ( i=0; i<b->nrefs; ++i ) {
		ref = b->ref[i];
		n = fields_find( ref, "REFNUM", 0 );
		if ( n==-1 ) n = build_refnum( ref, i+1 );
		if ( p->addcount ) {
			sprintf( buf, "_%ld", i+1 );
			newstr_strcat( &(ref->data[n]), buf );
		}
	}
}

static int
generate_citekey( fields *info, int nref )
{
	newstr citekey;
	int n1, n2;
	char *p, buf[100];
	newstr_init( &citekey );
	n1 = fields_find( info, "AUTHOR", 0 );
	if ( n1==-1 ) n1 = fields_find( info, "AUTHOR", -1 );
	n2 = fields_find( info, "YEAR", 0 );
	if ( n2==-1 ) n2 = fields_find( info, "YEAR", -1 );
	if ( n2==-1 ) n2 = fields_find( info, "PARTYEAR", 0 );
	if ( n2==-1 ) n2 = fields_find( info, "PARTYEAR", -1 );
	if ( n1!=-1 && n2!=-1 ) {
		p = info->data[n1].data;
		while ( p && *p && *p!='|' ) {
			if ( !is_ws( *p ) ) newstr_addchar( &citekey, *p ); 
			p++;
		}
		p = info->data[n2].data;
		while ( p && *p ) {
			if ( !is_ws( *p ) ) newstr_addchar( &citekey, *p );
			p++;
		}
		fields_add( info, "REFNUM", citekey.data, 0 );
	} else {
		sprintf( buf, "ref%d\n", nref );
		newstr_strcpy( &citekey, buf );
	}
	newstr_free( &citekey );
	return fields_find( info, "REFNUM", -1 );
}

static void
resolve_citekeys( bibl *b, list *citekeys, int *dup )
{
	char abc[]="abcdefghijklmnopqrstuvwxyz";
	newstr tmp;
	int nsame, ntmp, n, i, j;

	newstr_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]) );
			ntmp = nsame;
			while ( ntmp >= 26 ) {
				newstr_addchar( &tmp, 'a' );
					ntmp -= 26;
			}
			if ( ntmp<26 && ntmp>=0 )
			newstr_addchar( &tmp, abc[ntmp] );
			nsame++;
			dup[j] = -1;
			n = fields_find( b->ref[j], "REFNUM", -1 );
			if ( n!=-1 )
				newstr_newstrcpy(&((b->ref[j])->data[n]),&tmp);
		}
	}
	newstr_free( &tmp );
}

static void
get_citekeys( bibl *b, list *citekeys )
{
	fields *info;
	int i, n;
	for ( i=0; i<b->nrefs; ++i ) {
		info = b->ref[i];
		n = fields_find( info, "REFNUM", -1 );
		if ( n==-1 ) n = generate_citekey( info, i );
		if ( n!=-1 && info->data[n].data )
			list_add( citekeys, info->data[n].data );
		else
			list_add( citekeys, "" );
	}
}

static int 
dup_citekeys( bibl *b, list *citekeys )
{
	int i, j, *dup, ndup=0;
	dup = ( int * ) malloc( sizeof( int ) * citekeys->n );
	if ( !dup ) return 0;
	for ( i=0; i<citekeys->n; ++i ) dup[i] = -1;
	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 ) ) {
					dup[i] = i;
					dup[j] = i;
					ndup++;
			}
		}
	}
	if ( ndup ) resolve_citekeys( b, citekeys, dup );
	free( dup );
	return ndup;
}

static void
uniqueify_citekeys( bibl *b )
{
	list citekeys;
	list_init( &citekeys );
	get_citekeys( b, &citekeys );
	dup_citekeys( b, &citekeys );
	list_free( &citekeys );
}

static int 
convert_ref( bibl *bin, char *fname, bibl *bout, convert_rules *r, param *p )
{
	fields *rin, *rout;
	long i;
	int reftype;
	if ( r->cleanf ) r->cleanf( bin, p );
	for ( i=0; i<bin->nrefs; ++i ) {
		rin = bin->ref[i];
		rout = fields_new();
		if ( !rout ) return BIBL_ERR_MEMERR;
		if ( r->typef ) 
			reftype = r->typef( rin, fname, i+1, p, r->all, r->nall );
		else reftype = 0;
		r->convertf( rin, rout, reftype, p, r->all, r->nall );
		if ( r->all ) process_alwaysadd( rout, reftype, r );
		if ( p->verbose ) 
			bibl_verbose1( rout, rin, fname, i+1 );
		bibl_addref( bout, rout );
	}
	uniqueify_citekeys( bout );
	return BIBL_OK;
}

static void
rules_init( convert_rules *r, int mode )
{
	switch ( mode ) {
		case BIBL_BIBTEXIN:
			r->readf    = bibtexin_readf;
			r->processf = bibtexin_processf;
			r->cleanf   = bibtexin_cleanf;
			r->typef    = bibtexin_typef;
			r->convertf = bibtexin_convertf;
			r->all      = bibtex_all;
			r->nall     = bibtex_nall;
			break;
		case BIBL_BIBTEXOUT:
			r->headerf  = bibtexout_writeheader;
			r->footerf  = NULL;
			r->writef   = bibtexout_write;
			break;
		case BIBL_BIBLATEXIN:
			r->readf    = biblatexin_readf;
			r->processf = biblatexin_processf;
			r->cleanf   = biblatexin_cleanf;
			r->typef    = biblatexin_typef;
			r->convertf = biblatexin_convertf;
			r->all      = biblatex_all;
			r->nall     = biblatex_nall;
			break;
		case BIBL_ENDNOTEIN:
			r->readf    = endin_readf;
			r->cleanf   = endin_cleanf;
			r->processf = endin_processf;
			r->typef    = endin_typef;
			r->convertf = endin_convertf;
			r->all      = end_all;
			r->nall     = end_nall;
			break;
		case BIBL_ENDNOTEXMLIN:
			r->readf    = endxmlin_readf;
			r->cleanf   = NULL;
			r->processf = endxmlin_processf;
			r->typef    = endin_typef;
			r->convertf = endin_convertf;
			r->all      = end_all;
			r->nall     = end_nall;
			break;
		case BIBL_ENDNOTEOUT:
			r->headerf = endout_writeheader;
			r->footerf = NULL;
			r->writef  = endout_write;
			break;
		case BIBL_ADSABSOUT:
			r->headerf = adsout_writeheader;
			r->footerf = NULL;
			r->writef  = adsout_write;
			break;
		case BIBL_RISIN:
			r->readf    = risin_readf;
			r->cleanf   = NULL;
			r->processf = risin_processf;
			r->typef    = risin_typef;
			r->convertf = risin_convertf;
			r->all      = ris_all;
			r->nall     = ris_nall;
			break;
		case BIBL_RISOUT:
			r->headerf = risout_writeheader;
			r->footerf = NULL;
			r->writef  = risout_write;
			break;
		case BIBL_ISIIN:
			r->readf    = isiin_readf;
			r->cleanf   = NULL;
			r->processf = isiin_processf;
			r->typef    = isiin_typef;
			r->convertf = isiin_convertf;
			r->all      = isi_all;
			r->nall     = isi_nall;
			break;
		case BIBL_ISIOUT:
			r->headerf  = isiout_writeheader;
			r->footerf  = NULL;
			r->writef   = isiout_write;
			break;
		case BIBL_COPACIN:
			r->readf    = copacin_readf;
			r->cleanf   = NULL;
			r->processf = copacin_processf;
			r->typef    = NULL;
			r->convertf = copacin_convertf;
			r->all      = copac_all;
			r->nall     = copac_nall;
			break;
		case BIBL_MEDLINEIN:
			r->readf    = medin_readf;
			r->cleanf   = NULL;
			r->processf = medin_processf;
			r->typef    = NULL;
			r->convertf = NULL;
			r->all      = NULL;
			r->nall     = 0;
			break;
		case BIBL_WORDIN:
			r->readf    = wordin_readf;
			r->cleanf   = NULL;
			r->processf = wordin_processf;
			r->typef    = NULL;
			r->convertf = NULL;
			r->all      = NULL;
			r->nall     = 0;
			break;
		case BIBL_EBIIN:
			r->readf    = ebiin_readf;
			r->cleanf   = NULL;
			r->processf = ebiin_processf;
			r->typef    = NULL;
			r->convertf = NULL;
			r->all      = NULL;
			r->nall     = 0;
			break;
		case BIBL_MODSIN:
			r->readf    = modsin_readf;
			r->cleanf   = NULL;
			r->processf = modsin_processf;
			r->typef    = NULL;
			r->convertf = NULL;
			r->all      = NULL;
			r->nall     = 0;
			break;
		case BIBL_MODSOUT:
			r->headerf = modsout_writeheader;
			r->footerf = modsout_writefooter;
			r->writef  = modsout_write;
			break;
		case BIBL_WORD2007OUT:
			r->headerf = wordout_writeheader;
			r->footerf = wordout_writefooter;
			r->writef  = wordout_write;
			break;
		default:
			break;
	}
}

static void
bibl_setreadparams( param *np, param *op )
{
	bibl_duplicateparams( np, op );
/*
	list_init( &(np->asis) );
	list_init( &(np->corps) );
	if ( !op->progname ) np->progname = NULL;
	else np->progname = strdup( op->progname );
	np->readformat = op->readformat;
	np->writeformat = op->writeformat;
	np->latexin = op->latexin;
	np->utf8in = op->utf8in;
	np->xmlin = op->xmlin;
	np->charsetin = op->charsetin;
	np->charsetin_src = op->charsetin_src;
	np->verbose = op->verbose;
	np->format_opts = op->format_opts;
	np->addcount = op->addcount;
	np->output_raw = op->output_raw;
	list_copy( &(np->asis), &(op->asis) );
	list_copy( &(np->corps), &(op->corps) );*/
	np->utf8out = 1;
	np->charsetout = BIBL_CHARSET_UNICODE;
	np->charsetout_src = BIBL_SRC_DEFAULT;
	np->xmlout = 0;
	np->latexout = 0;
}

int
bibl_read( bibl *b, FILE *fp, char *filename, param *p )
{
	convert_rules r;
	param lp;
	bibl bin;

	if ( !b ) return BIBL_ERR_BADINPUT;
	if ( !fp ) return BIBL_ERR_BADINPUT;
	if ( !p ) return BIBL_ERR_BADINPUT;
	if ( bibl_illegalinmode( p->readformat ) ) return BIBL_ERR_BADINPUT;

	bibl_setreadparams( &lp, p );
	bibl_init( &bin );
	rules_init( &r, p->readformat );
	read_ref( fp, &bin, filename, &r, &lp );
	if ( !lp.output_raw || ( lp.output_raw & BIBL_RAW_WITHCHARCONVERT ) )
		bibl_fixcharsets( &bin, &lp );
	if ( !lp.output_raw )
		convert_ref( &bin, filename, b, &r, &lp );
	else {
		if ( p->verbose > 1 ) bibl_verbose0( &bin );
		bibl_copy( b, &bin );
	}
	if ( !lp.output_raw || ( lp.output_raw & BIBL_RAW_WITHMAKEREFID ) )
		bibl_checkrefid( b, &lp );
	bibl_free( &bin );

	return BIBL_OK;
}

static FILE *
singlerefname( fields *reffields, long nref, int mode )
{
	char outfile[2048];
	char suffix[5] = "xml";
	FILE *fp;
	long count;
	int  found;
	if ( mode==BIBL_BIBTEXOUT ) strcpy( suffix, "bib" );
	else if ( mode==BIBL_RISOUT ) strcpy( suffix, "ris" );
	else if ( mode==BIBL_ENDNOTEOUT ) strcpy( suffix, "end" );
	else if ( mode==BIBL_ADSABSOUT ) strcpy( suffix, "ads" );
	found = fields_find( reffields, "REFNUM", 0 );
	/* find new filename based on reference */
	if ( found!=-1 ) {
		sprintf( outfile,"%s.%s",reffields->data[found].data, suffix );
	} else  sprintf( outfile,"%ld.%s",nref, suffix );
	count = 0;
	fp = fopen( outfile, "r" );
	while ( fp ) {
		fclose(fp);
		count++;
		if ( count==60000 ) return NULL;
		if ( found!=-1 )
			sprintf( outfile, "%s_%ld.%s", 
				reffields->data[found].data, count, suffix  );
		else sprintf( outfile,"%ld_%ld.%s",nref, count, suffix );
		fp = fopen( outfile, "r" );
	}
	return fopen( outfile, "w" );
}

static int
output_bibl( FILE *fp, bibl *b, convert_rules *r, param *p )
{
	long i;
	if ( !p->singlerefperfile && r->headerf ) r->headerf( fp, p );
	for ( i=0; i<b->nrefs; ++i ) {
		if ( p->singlerefperfile ) { 
			fp = singlerefname( b->ref[i], i, p->writeformat );
			if ( fp ) {
				if ( r->headerf ) r->headerf( fp, p );
			} else return BIBL_ERR_CANTOPEN;
		}
		r->writef( b->ref[i], fp, p, i );
		if ( p->singlerefperfile ) {
			if ( r->footerf ) r->footerf( fp );
			fclose( fp );
		}
	}
	if ( !p->singlerefperfile && r->footerf ) r->footerf( fp );
	return 1;
}

static void
bibl_setwriteparams( param *np, param *op )
{
	bibl_duplicateparams( np, op );
/*
	list_init( &(np->asis) );
	list_init( &(np->corps) );
	list_copy( &(np->asis), &(op->asis ) );
	list_copy( &(np->corps), &(op->corps ) );
	if ( !op->progname ) np->progname = NULL;
	else np->progname = strdup( op->progname );
	np->readformat = op->readformat;
	np->writeformat = op->writeformat;
	np->utf8out = op->utf8out;
	np->utf8bom = op->utf8bom;
	np->charsetout = op->charsetout;
	np->charsetout_src = op->charsetout_src;
	np->latexout = op->latexout;
	np->xmlout = op->xmlout;
	np->verbose = op->verbose;
	np->format_opts = op->format_opts;
	np->addcount = op->addcount;
	np->output_raw = op->output_raw;
	np->singlerefperfile = op->singlerefperfile;*/
	np->xmlin = 0;
	np->latexin = 0;
	np->utf8in = 1;
	np->charsetin = BIBL_CHARSET_UNICODE;
	np->charsetin_src = BIBL_SRC_DEFAULT;
}

int
bibl_write( bibl *b, FILE *fp, param *p )
{
	convert_rules r;
	param lp;

	if ( !b ) return BIBL_ERR_BADINPUT;
	if ( !p ) return BIBL_ERR_BADINPUT;
	if ( bibl_illegaloutmode( p->writeformat ) ) return BIBL_ERR_BADINPUT;
	if ( !fp && ( !p || !p->singlerefperfile ) ) return BIBL_ERR_BADINPUT;

	rules_init( &r, p->writeformat );
	bibl_setwriteparams( &lp, p );
	bibl_fixcharsets( b, &lp );
	output_bibl( fp, b, &r, &lp );

	return BIBL_OK;
}