1 /*
2  * biblatexout.c
3  *
4  * Copyright (c) Chris Putnam 2003-2020
5  *
6  * Program and source code released under the GPL version 2
7  *
8  */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include "str.h"
14 #include "strsearch.h"
15 #include "utf8.h"
16 #include "xml.h"
17 #include "fields.h"
18 #include "generic.h"
19 #include "name.h"
20 #include "title.h"
21 #include "type.h"
22 #include "url.h"
23 #include "bibformats.h"
24 
25 /*****************************************************
26  PUBLIC: int biblatexout_initparams()
27 *****************************************************/
28 
29 static int  biblatexout_write( fields *in, FILE *fp, param *p, unsigned long refnum );
30 static int  biblatexout_assemble( fields *in, fields *out, param *pm, unsigned long refnum );
31 
32 int
biblatexout_initparams(param * pm,const char * progname)33 biblatexout_initparams( param *pm, const char *progname )
34 {
35 	pm->writeformat      = BIBL_BIBLATEXOUT;
36 	pm->format_opts      = 0;
37 	pm->charsetout       = BIBL_CHARSET_DEFAULT;
38 	pm->charsetout_src   = BIBL_SRC_DEFAULT;
39 	pm->latexout         = 1;
40 	pm->utf8out          = BIBL_CHARSET_UTF8_DEFAULT;
41 	pm->utf8bom          = BIBL_CHARSET_BOM_DEFAULT;
42 	pm->xmlout           = BIBL_XMLOUT_FALSE;
43 	pm->nosplittitle     = 0;
44 	pm->verbose          = 0;
45 	pm->addcount         = 0;
46 	pm->singlerefperfile = 0;
47 
48 	pm->headerf   = generic_writeheader;
49 	pm->footerf   = NULL;
50 	pm->assemblef = biblatexout_assemble;
51 	pm->writef    = biblatexout_write;
52 
53 	if ( !pm->progname ) {
54 		if ( !progname ) pm->progname = NULL;
55 		else {
56 			pm->progname = strdup( progname );
57 			if ( !pm->progname ) return BIBL_ERR_MEMERR;
58 		}
59 	}
60 
61 	return BIBL_OK;
62 }
63 
64 /*****************************************************
65  PUBLIC: int biblatexout_assemble()
66 *****************************************************/
67 
68 enum {
69 	TYPE_UNKNOWN = 0,
70 	TYPE_ARTICLE,
71 	TYPE_SUPPPERIODICAL,
72 	TYPE_INBOOK,
73 	TYPE_INPROCEEDINGS,
74 	TYPE_PROCEEDINGS,
75 	TYPE_CONFERENCE,       /* legacy */
76 	TYPE_INCOLLECTION,
77 	TYPE_COLLECTION,
78 	TYPE_SUPPCOLLECTION,
79 	TYPE_REFERENCE,
80 	TYPE_MVREFERENCE,
81 	TYPE_BOOK,
82 	TYPE_BOOKLET,
83 	TYPE_SUPPBOOK,
84 	TYPE_PHDTHESIS,        /* legacy */
85 	TYPE_MASTERSTHESIS,    /* legacy */
86 	TYPE_DIPLOMATHESIS,
87 	TYPE_REPORT,
88 	TYPE_TECHREPORT,
89 	TYPE_MANUAL,
90 	TYPE_UNPUBLISHED,
91 	TYPE_PATENT,
92 	TYPE_ELECTRONIC,       /* legacy */
93 	TYPE_ONLINE,
94 	TYPE_WWW,              /* jurabib compatibility */
95 	TYPE_MISC,
96 	NUM_TYPES
97 };
98 
99 static int
biblatexout_type(fields * in,const char * progname,const char * filename,unsigned long refnum)100 biblatexout_type( fields *in, const char *progname, const char *filename, unsigned long refnum )
101 {
102 	match_type genre_matches[] = {
103 		{ "periodical",             TYPE_ARTICLE,       LEVEL_ANY  },
104 		{ "academic journal",       TYPE_ARTICLE,       LEVEL_ANY  },
105 		{ "magazine",               TYPE_ARTICLE,       LEVEL_ANY  },
106 		{ "newspaper",              TYPE_ARTICLE,       LEVEL_ANY  },
107 		{ "article",                TYPE_ARTICLE,       LEVEL_ANY  },
108 		{ "instruction",            TYPE_MANUAL,        LEVEL_ANY  },
109 		{ "book",                   TYPE_BOOK,          LEVEL_MAIN },
110 		{ "booklet",                TYPE_BOOKLET,       LEVEL_MAIN },
111 		{ "book",                   TYPE_INBOOK,        LEVEL_ANY  },
112 		{ "book chapter",           TYPE_INBOOK,        LEVEL_ANY  },
113 		{ "unpublished",            TYPE_UNPUBLISHED,   LEVEL_ANY  },
114 		{ "manuscript",             TYPE_UNPUBLISHED,   LEVEL_ANY  },
115 		{ "conference publication", TYPE_PROCEEDINGS,   LEVEL_MAIN },
116 		{ "conference publication", TYPE_INPROCEEDINGS, LEVEL_ANY  },
117 		{ "collection",             TYPE_COLLECTION,    LEVEL_MAIN },
118 		{ "collection",             TYPE_INCOLLECTION,  LEVEL_ANY  },
119 		{ "report",                 TYPE_REPORT,        LEVEL_ANY  },
120 		{ "technical report",       TYPE_TECHREPORT,    LEVEL_ANY  },
121 		{ "Masters thesis",         TYPE_MASTERSTHESIS, LEVEL_ANY  },
122 		{ "Diploma thesis",         TYPE_DIPLOMATHESIS, LEVEL_ANY  },
123 		{ "Ph.D. thesis",           TYPE_PHDTHESIS,     LEVEL_ANY  },
124 		{ "Licentiate thesis",      TYPE_PHDTHESIS,     LEVEL_ANY  },
125 		{ "thesis",                 TYPE_PHDTHESIS,     LEVEL_ANY  },
126 		{ "electronic",             TYPE_ELECTRONIC,    LEVEL_ANY  },
127 		{ "patent",                 TYPE_PATENT,        LEVEL_ANY  },
128 		{ "miscellaneous",          TYPE_MISC,          LEVEL_ANY  },
129 	};
130 	int ngenre_matches = sizeof( genre_matches ) / sizeof( genre_matches[0] );
131 
132 	match_type resource_matches[] = {
133 		{ "moving image",           TYPE_ELECTRONIC,    LEVEL_ANY  },
134 		{ "software, multimedia",   TYPE_ELECTRONIC,    LEVEL_ANY  },
135 	};
136 	int nresource_matches = sizeof( resource_matches ) /sizeof( resource_matches[0] );
137 
138 	match_type issuance_matches[] = {
139 		{ "monographic",            TYPE_BOOK,          LEVEL_MAIN },
140 		{ "monographic",            TYPE_INBOOK,        LEVEL_ANY  },
141 	};
142 	int nissuance_matches = sizeof( issuance_matches ) / sizeof( issuance_matches[0] );
143 
144 	int type, maxlevel, n;
145 
146 	type = type_from_mods_hints( in, TYPE_FROM_GENRE, genre_matches, ngenre_matches, TYPE_UNKNOWN );
147 	if ( type==TYPE_UNKNOWN ) type = type_from_mods_hints( in, TYPE_FROM_RESOURCE, resource_matches, nresource_matches, TYPE_UNKNOWN );
148 	if ( type==TYPE_UNKNOWN ) type = type_from_mods_hints( in, TYPE_FROM_ISSUANCE, issuance_matches, nissuance_matches, TYPE_UNKNOWN );
149 
150 	/* default to TYPE_MISC */
151 	if ( type==TYPE_UNKNOWN ) {
152 		maxlevel = fields_maxlevel( in );
153 		if ( maxlevel > 0 ) type = TYPE_MISC;
154 		else {
155 			if ( progname ) REprintf( "%s: ", progname );
156 			REprintf( "Cannot identify TYPE in reference %lu ", refnum+1 );
157 			n = fields_find( in, "REFNUM", LEVEL_ANY );
158 			if ( n!=FIELDS_NOTFOUND )
159 				REprintf( " %s", (char*) fields_value( in, n, FIELDS_CHRP ) );
160 			REprintf( " (defaulting to @Misc)\n" );
161 			type = TYPE_MISC;
162 		}
163 	}
164 	return type;
165 }
166 
167 static void
append_type(int type,fields * out,int * status)168 append_type( int type, fields *out, int *status )
169 {
170 	char *typenames[ NUM_TYPES ] = {
171 		[ TYPE_ARTICLE        ] = "Article",
172 		[ TYPE_SUPPPERIODICAL ] = "SuppPeriodical",
173 		[ TYPE_INBOOK         ] = "Inbook",
174 		[ TYPE_PROCEEDINGS    ] = "Proceedings",
175 		[ TYPE_INPROCEEDINGS  ] = "InProceedings",
176 		[ TYPE_CONFERENCE     ] = "Conference",
177 		[ TYPE_BOOK           ] = "Book",
178 		[ TYPE_BOOKLET        ] = "Booklet",
179 		[ TYPE_SUPPBOOK       ] = "SuppBook",
180 		[ TYPE_PHDTHESIS      ] = "PhdThesis",
181 		[ TYPE_MASTERSTHESIS  ] = "MastersThesis",
182 		[ TYPE_DIPLOMATHESIS  ] = "MastersThesis",
183 		[ TYPE_REPORT         ] = "Report",
184 		[ TYPE_TECHREPORT     ] = "TechReport",
185 		[ TYPE_REFERENCE      ] = "Reference",
186 		[ TYPE_MVREFERENCE    ] = "MvReference",
187 		[ TYPE_MANUAL         ] = "Manual",
188 		[ TYPE_COLLECTION     ] = "Collection",
189 		[ TYPE_SUPPCOLLECTION ] = "SuppCollection",
190 		[ TYPE_INCOLLECTION   ] = "InCollection",
191 		[ TYPE_UNPUBLISHED    ] = "Unpublished",
192 		[ TYPE_ELECTRONIC     ] = "Electronic",
193 		[ TYPE_ONLINE         ] = "Online",
194 		[ TYPE_WWW            ] = "WWW",
195 		[ TYPE_PATENT         ] = "Patent",
196 		[ TYPE_MISC           ] = "Misc",
197 	};
198 	int fstatus;
199 	char *s;
200 
201 	if ( type < 0 || type >= NUM_TYPES ) type = TYPE_MISC;
202 	s = typenames[ type ];
203 
204 	fstatus = fields_add( out, "TYPE", s, LEVEL_MAIN );
205 	if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
206 }
207 
208 static void
append_citekey(fields * in,fields * out,int format_opts,int * status)209 append_citekey( fields *in, fields *out, int format_opts, int *status )
210 {
211 	int n, fstatus;
212 	str s;
213 	char *p;
214 
215 	n = fields_find( in, "REFNUM", LEVEL_ANY );
216 	if ( ( format_opts & BIBL_FORMAT_BIBOUT_DROPKEY ) || n==FIELDS_NOTFOUND ) {
217 		fstatus = fields_add( out, "REFNUM", "", LEVEL_MAIN );
218 		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
219 	}
220 
221 	else {
222 		str_init( &s );
223 		p = fields_value( in, n, FIELDS_CHRP );
224 		while ( p && *p && *p!='|' ) {
225 			if ( format_opts & BIBL_FORMAT_BIBOUT_STRICTKEY ) {
226 				if ( isdigit((unsigned char)*p) || (*p>='A' && *p<='Z') ||
227 				     (*p>='a' && *p<='z' ) ) {
228 					str_addchar( &s, *p );
229 				}
230 			}
231 			else {
232 				if ( *p!=' ' && *p!='\t' ) {
233 					str_addchar( &s, *p );
234 				}
235 			}
236 			p++;
237 		}
238 		if ( str_memerr( &s ) )  { *status = BIBL_ERR_MEMERR; str_free( &s ); return; }
239 		fstatus = fields_add( out, "REFNUM", str_cstr( &s ), LEVEL_MAIN );
240 		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
241 		str_free( &s );
242 	}
243 }
244 
245 static void
append_simple(fields * in,char * intag,char * outtag,fields * out,int * status)246 append_simple( fields *in, char *intag, char *outtag, fields *out, int *status )
247 {
248 	int n, fstatus;
249 
250 	n = fields_find( in, intag, LEVEL_ANY );
251 	if ( n!=FIELDS_NOTFOUND ) {
252 		fields_set_used( in, n );
253 		fstatus = fields_add( out, outtag, fields_value( in, n, FIELDS_CHRP ), LEVEL_MAIN );
254 		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
255 	}
256 }
257 
258 static void
append_simpleall(fields * in,char * intag,char * outtag,fields * out,int * status)259 append_simpleall( fields *in, char *intag, char *outtag, fields *out, int *status )
260 {
261 	int i, fstatus;
262 
263 	for ( i=0; i<in->n; ++i ) {
264 		if ( fields_match_tag( in, i, intag ) ) {
265 			fields_set_used( in, i );
266 			fstatus = fields_add( out, outtag, fields_value( in, i, FIELDS_CHRP ), LEVEL_MAIN );
267 			if ( fstatus!=FIELDS_OK ) {
268 				*status = BIBL_ERR_MEMERR;
269 				return;
270 			}
271 		}
272 	}
273 }
274 
275 static void
append_keywords(fields * in,fields * out,int * status)276 append_keywords( fields *in, fields *out, int *status )
277 {
278 	str keywords, *word;
279 	vplist_index i;
280 	int fstatus;
281 	vplist a;
282 
283 	str_init( &keywords );
284 	vplist_init( &a );
285 
286 	fields_findv_each( in, LEVEL_ANY, FIELDS_STRP, &a, "KEYWORD" );
287 
288 	if ( a.n ) {
289 
290 		for ( i=0; i<a.n; ++i ) {
291 			word = vplist_get( &a, i );
292 			if ( i>0 ) str_strcatc( &keywords, "; " );
293 			str_strcat( &keywords, word );
294 		}
295 
296 		if ( str_memerr( &keywords ) ) { *status = BIBL_ERR_MEMERR; goto out; }
297 
298 		fstatus = fields_add( out, "keywords", str_cstr( &keywords ), LEVEL_MAIN );
299 		if ( fstatus!=FIELDS_OK ) {
300 			*status = BIBL_ERR_MEMERR;
301 			goto out;
302 		}
303 
304 
305 	}
306 
307 out:
308 	str_free( &keywords );
309 	vplist_free( &a );
310 }
311 
312 static void
append_fileattach(fields * in,fields * out,int * status)313 append_fileattach( fields *in, fields *out, int *status )
314 {
315 	char *tag, *value;
316 	int i, fstatus;
317 	str data;
318 
319 	str_init( &data );
320 
321 	for ( i=0; i<in->n; ++i ) {
322 
323 		tag = fields_tag( in, i, FIELDS_CHRP );
324 		if ( strcasecmp( tag, "FILEATTACH" ) ) continue;
325 
326 		value = fields_value( in, i, FIELDS_CHRP );
327 		str_strcpyc( &data, ":" );
328 		str_strcatc( &data, value );
329 		if ( strsearch( value, ".pdf" ) )
330 			str_strcatc( &data, ":PDF" );
331 		else if ( strsearch( value, ".html" ) )
332 			str_strcatc( &data, ":HTML" );
333 		else str_strcatc( &data, ":TYPE" );
334 
335 		if ( str_memerr( &data ) ) {
336 			*status = BIBL_ERR_MEMERR;
337 			goto out;
338 		}
339 
340 		fields_set_used( in, i );
341 		fstatus = fields_add( out, "file", str_cstr( &data ), LEVEL_MAIN );
342 		if ( fstatus!=FIELDS_OK ) {
343 			*status = BIBL_ERR_MEMERR;
344 			goto out;
345 		}
346 
347 		str_empty( &data );
348 	}
349 out:
350 	str_free( &data );
351 }
352 
353 static void
append_people(fields * in,char * tag,char * ctag,char * atag,char * bibtag,int level,fields * out,int format_opts,int latex_out,int * status)354 append_people( fields *in, char *tag, char *ctag, char *atag,
355 		char *bibtag, int level, fields *out, int format_opts, int latex_out, int *status )
356 {
357 	int i, npeople, person, corp, asis, fstatus;
358 	str allpeople, oneperson;
359 
360 	strs_init( &allpeople, &oneperson, NULL );
361 
362 	/* primary citation authors */
363 	npeople = 0;
364 	for ( i=0; i<in->n; ++i ) {
365 		if ( level!=LEVEL_ANY && in->level[i]!=level ) continue;
366 		person = ( strcasecmp( in->tag[i].data, tag ) == 0 );
367 		corp   = ( strcasecmp( in->tag[i].data, ctag ) == 0 );
368 		asis   = ( strcasecmp( in->tag[i].data, atag ) == 0 );
369 		if ( person || corp || asis ) {
370 			if ( npeople>0 ) {
371 				if ( format_opts & BIBL_FORMAT_BIBOUT_WHITESPACE )
372 					str_strcatc( &allpeople, "\n\t\tand " );
373 				else str_strcatc( &allpeople, "\nand " );
374 			}
375 			if ( corp ) {
376 				if ( latex_out ) str_addchar( &allpeople, '{' );
377 				str_strcat( &allpeople, fields_value( in, i, FIELDS_STRP ) );
378 				if ( latex_out ) str_addchar( &allpeople, '}' );
379 			} else if ( asis ) {
380 				if ( latex_out ) str_addchar( &allpeople, '{' );
381 				str_strcat( &allpeople, fields_value( in, i, FIELDS_STRP ) );
382 				if ( latex_out ) str_addchar( &allpeople, '}' );
383 			} else {
384 				name_build_withcomma( &oneperson, fields_value( in, i, FIELDS_CHRP ) );
385 				str_strcat( &allpeople, &oneperson );
386 			}
387 			npeople++;
388 		}
389 	}
390 	if ( npeople ) {
391 		fstatus = fields_add( out, bibtag, allpeople.data, LEVEL_MAIN );
392 		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
393 	}
394 
395 	strs_free( &allpeople, &oneperson, NULL );
396 }
397 
398 static int
append_title_chosen(fields * in,char * bibtag,fields * out,int nmainttl,int nsubttl)399 append_title_chosen( fields *in, char *bibtag, fields *out, int nmainttl, int nsubttl )
400 {
401 	str fulltitle, *mainttl = NULL, *subttl = NULL;
402 	int status, ret = BIBL_OK;
403 
404 	str_init( &fulltitle );
405 
406 	if ( nmainttl!=-1 ) {
407 		mainttl = fields_value( in, nmainttl, FIELDS_STRP );
408 		fields_set_used( in, nmainttl );
409 	}
410 
411 	if ( nsubttl!=-1 ) {
412 		subttl = fields_value( in, nsubttl, FIELDS_STRP );
413 		fields_set_used( in, nsubttl );
414 	}
415 
416 	title_combine( &fulltitle, mainttl, subttl );
417 
418 	if ( str_memerr( &fulltitle ) ) {
419 		ret = BIBL_ERR_MEMERR;
420 		goto out;
421 	}
422 
423 	if ( str_has_value( &fulltitle ) ) {
424 		status = fields_add( out, bibtag, str_cstr( &fulltitle ), LEVEL_MAIN );
425 		if ( status!=FIELDS_OK ) ret = BIBL_ERR_MEMERR;
426 	}
427 
428 out:
429 	str_free( &fulltitle );
430 	return ret;
431 }
432 
433 static int
append_title(fields * in,char * bibtag,int level,fields * out,int format_opts)434 append_title( fields *in, char *bibtag, int level, fields *out, int format_opts )
435 {
436 	int title, short_title, subtitle, short_subtitle, use_title, use_subtitle;
437 
438 	title          = fields_find( in, "TITLE",         level );
439 	short_title    = fields_find( in, "SHORTTITLE",    level );
440 	subtitle       = fields_find( in, "SUBTITLE",      level );
441 	short_subtitle = fields_find( in, "SHORTSUBTITLE", level );
442 
443 	if ( title==FIELDS_NOTFOUND || ( ( format_opts & BIBL_FORMAT_BIBOUT_SHORTTITLE ) && level==1 ) ) {
444 		use_title    = short_title;
445 		use_subtitle = short_subtitle;
446 	}
447 
448 	else {
449 		use_title    = title;
450 		use_subtitle = subtitle;
451 	}
452 
453 	return append_title_chosen( in, bibtag, out, use_title, use_subtitle );
454 }
455 
456 static void
append_titles(fields * in,int type,fields * out,int format_opts,int * status)457 append_titles( fields *in, int type, fields *out, int format_opts, int *status )
458 {
459 	/* item=main level title */
460 	*status = append_title( in, "title", 0, out, format_opts );
461 	if ( *status!=BIBL_OK ) return;
462 
463 	switch( type ) {
464 
465 		case TYPE_ARTICLE:
466 		*status = append_title( in, "journal", 1, out, format_opts );
467 		break;
468 
469 		case TYPE_INBOOK:
470 		*status = append_title( in, "bookTitle", 1, out, format_opts );
471 		if ( *status!=BIBL_OK ) return;
472 		*status = append_title( in, "series",    2, out, format_opts );
473 		break;
474 
475 		case TYPE_INCOLLECTION:
476 		case TYPE_INPROCEEDINGS:
477 		*status = append_title( in, "booktitle", 1, out, format_opts );
478 		if ( *status!=BIBL_OK ) return;
479 		*status = append_title( in, "series",    2, out, format_opts );
480 		break;
481 
482 		case TYPE_PHDTHESIS:
483 		case TYPE_MASTERSTHESIS:
484 		*status = append_title( in, "series", 1, out, format_opts );
485 		break;
486 
487 		case TYPE_BOOK:
488 		case TYPE_REPORT:
489 		case TYPE_COLLECTION:
490 		case TYPE_PROCEEDINGS:
491 		*status = append_title( in, "series", 1, out, format_opts );
492 		if ( *status!=BIBL_OK ) return;
493 		*status = append_title( in, "series", 2, out, format_opts );
494 		break;
495 
496 		default:
497 		/* do nothing */
498 		break;
499 
500 	}
501 }
502 
503 static int
find_date(fields * in,char * date_element)504 find_date( fields *in, char *date_element )
505 {
506 	char date[100], partdate[100];
507 	int n;
508 
509 	sprintf( date, "DATE:%s", date_element );
510 	n = fields_find( in, date, LEVEL_ANY );
511 
512 	if ( n==FIELDS_NOTFOUND ) {
513 		sprintf( partdate, "PARTDATE:%s", date_element );
514 		n = fields_find( in, partdate, LEVEL_ANY );
515 	}
516 
517 	return n;
518 }
519 
520 static void
append_date(fields * in,fields * out,int * status)521 append_date( fields *in, fields *out, int *status )
522 {
523 	char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
524 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
525 	int n, month, fstatus;
526 
527 	n = find_date( in, "YEAR" );
528 	if ( n!=FIELDS_NOTFOUND ) {
529 		fields_set_used( in, n );
530 		fstatus = fields_add( out, "year", fields_value( in, n, FIELDS_CHRP ), LEVEL_MAIN );
531 		if ( fstatus!=FIELDS_OK ) {
532 			*status = BIBL_ERR_MEMERR;
533 			return;
534 		}
535 	}
536 
537 	n = find_date( in, "MONTH" );
538 	if ( n!=-1 ) {
539 		fields_set_used( in, n );
540 		month = atoi( fields_value( in, n, FIELDS_CHRP ) );
541 		if ( month>0 && month<13 )
542 			fstatus = fields_add( out, "month", months[month-1], LEVEL_MAIN );
543 		else
544 			fstatus = fields_add( out, "month", fields_value( in, n, FIELDS_CHRP ), LEVEL_MAIN );
545 		if ( fstatus!=FIELDS_OK ) {
546 			*status = BIBL_ERR_MEMERR;
547 			return;
548 		}
549 	}
550 
551 	n = find_date( in, "DAY" );
552 	if ( n!=-1 ) {
553 		fields_set_used( in, n );
554 		fstatus = fields_add( out, "day", fields_value( in, n, FIELDS_CHRP ), LEVEL_MAIN );
555 		if ( fstatus!=FIELDS_OK ) {
556 			*status = BIBL_ERR_MEMERR;
557 			return;
558 		}
559 	}
560 
561 }
562 
563 static void
append_arxiv(fields * in,fields * out,int * status)564 append_arxiv( fields *in, fields *out, int *status )
565 {
566 	int n, fstatus1, fstatus2;
567 	str url;
568 
569 	n = fields_find( in, "ARXIV", LEVEL_ANY );
570 	if ( n==FIELDS_NOTFOUND ) return;
571 
572 	fields_set_used( in, n );
573 
574 	/* ...write:
575 	 *     archivePrefix = "arXiv",
576 	 *     eprint = "#####",
577 	 * ...for arXiv references
578 	 */
579 	fstatus1 = fields_add( out, "archivePrefix", "arXiv", LEVEL_MAIN );
580 	fstatus2 = fields_add( out, "eprint", fields_value( in, n, FIELDS_CHRP ), LEVEL_MAIN );
581 	if ( fstatus1!=FIELDS_OK || fstatus2!=FIELDS_OK ) {
582 		*status = BIBL_ERR_MEMERR;
583 		return;
584 	}
585 
586 	/* ...also write:
587 	 *     url = "http://arxiv.org/abs/####",
588 	 * ...to maximize compatibility
589 	 */
590 	str_init( &url );
591 	arxiv_to_url( in, n, "URL", &url );
592 	if ( str_has_value( &url ) ) {
593 		fstatus1 = fields_add( out, "url", str_cstr( &url ), LEVEL_MAIN );
594 		if ( fstatus1!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
595 	}
596 	str_free( &url );
597 }
598 
599 static void
append_urls(fields * in,fields * out,int * status)600 append_urls( fields *in, fields *out, int *status )
601 {
602 	int lstatus;
603 	slist types;
604 
605 	lstatus = slist_init_valuesc( &types, "URL", "DOI", "PMID", "PMC", "JSTOR", NULL );
606 	if ( lstatus!=SLIST_OK ) {
607 		*status = BIBL_ERR_MEMERR;
608 		return;
609 	}
610 
611 	*status = urls_merge_and_add( in, LEVEL_ANY, out, "url", LEVEL_MAIN, &types );
612 
613 	slist_free( &types );
614 }
615 
616 static void
append_isi(fields * in,fields * out,int * status)617 append_isi( fields *in, fields *out, int *status )
618 {
619 	int n, fstatus;
620 
621 	n = fields_find( in, "ISIREFNUM", LEVEL_ANY );
622 	if ( n==FIELDS_NOTFOUND ) return;
623 
624 	fstatus = fields_add( out, "note", fields_value( in, n, FIELDS_CHRP ), LEVEL_MAIN );
625 	if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
626 }
627 
628 static void
append_articlenumber(fields * in,fields * out,int * status)629 append_articlenumber( fields *in, fields *out, int *status )
630 {
631 	int n, fstatus;
632 
633 	n = fields_find( in, "ARTICLENUMBER", LEVEL_ANY );
634 	if ( n==FIELDS_NOTFOUND ) return;
635 
636 	fields_set_used( in, n );
637 	fstatus = fields_add( out, "pages", fields_value( in, n, FIELDS_CHRP ), LEVEL_MAIN );
638 	if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
639 }
640 
641 static int
pages_build_pagestr(str * pages,fields * in,int sn,int en,int format_opts)642 pages_build_pagestr( str *pages, fields *in, int sn, int en, int format_opts )
643 {
644 	/* ...append if starting page number is defined */
645 	if ( sn!=-1 ) {
646 		str_strcat( pages, fields_value( in, sn, FIELDS_STRP ) );
647 		fields_set_used( in, sn );
648 	}
649 
650 	/* ...append dashes if both starting and ending page numbers are defined */
651 	if ( sn!=-1 && en!=-1 ) {
652 		if ( format_opts & BIBL_FORMAT_BIBOUT_SINGLEDASH )
653 			str_strcatc( pages, "-" );
654 		else
655 			str_strcatc( pages, "--" );
656 	}
657 
658 	/* ...append ending page number is defined */
659 	if ( en!=-1 ) {
660 		str_strcat( pages, fields_value( in, en, FIELDS_STRP ) );
661 		fields_set_used( in, en );
662 	}
663 
664 	if ( str_memerr( pages ) ) return BIBL_ERR_MEMERR;
665 	else return BIBL_OK;
666 }
667 
668 static int
pages_are_defined(fields * in,int * sn,int * en)669 pages_are_defined( fields *in, int *sn, int *en )
670 {
671 	*sn = fields_find( in, "PAGES:START", LEVEL_ANY );
672 	*en = fields_find( in, "PAGES:STOP",  LEVEL_ANY );
673 	if ( *sn==FIELDS_NOTFOUND && *en==FIELDS_NOTFOUND ) return 0;
674 	else return 1;
675 }
676 
677 static void
append_pages(fields * in,fields * out,int format_opts,int * status)678 append_pages( fields *in, fields *out, int format_opts, int *status )
679 {
680 	int sn, en, fstatus;
681 	str pages;
682 
683 	if ( !pages_are_defined( in, &sn, &en ) ) {
684 		append_articlenumber( in, out, status );
685 		return;
686 	}
687 
688 	str_init( &pages );
689 	*status = pages_build_pagestr( &pages, in, sn, en, format_opts );
690 	if ( *status==BIBL_OK ) {
691 		fstatus = fields_add( out, "pages", str_cstr( &pages ), LEVEL_MAIN );
692 		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
693 	}
694 	str_free( &pages );
695 }
696 
697 /*
698  * from Tim Hicks:
699  * I'm no expert on bibtex, but those who know more than I on our mailing
700  * list suggest that 'issue' isn't a recognised key for bibtex and
701  * therefore that bibutils should be aliasing IS to number at some point in
702  * the conversion.
703  *
704  * Therefore prefer outputting issue/number as number and only keep
705  * a distinction if both issue and number are present for a particular
706  * reference.
707  */
708 
709 static void
append_issue_number(fields * in,fields * out,int * status)710 append_issue_number( fields *in, fields *out, int *status )
711 {
712 	char issue[] = "issue", number[] = "number", *use_issue = number;
713 	int nissue  = fields_find( in, "ISSUE",  LEVEL_ANY );
714 	int nnumber = fields_find( in, "NUMBER", LEVEL_ANY );
715 	int fstatus;
716 
717 	if ( nissue!=FIELDS_NOTFOUND && nnumber!=FIELDS_NOTFOUND ) use_issue = issue;
718 
719 	if ( nissue!=FIELDS_NOTFOUND ) {
720 		fields_set_used( in, nissue );
721 		fstatus = fields_add( out, use_issue, fields_value( in, nissue, FIELDS_CHRP ), LEVEL_MAIN );
722 		if ( fstatus!=FIELDS_OK ) {
723 			*status = BIBL_ERR_MEMERR;
724 			return;
725 		}
726 	}
727 
728 	if ( nnumber!=FIELDS_NOTFOUND ) {
729 		fields_set_used( in, nnumber );
730 		fstatus = fields_add( out, "number", fields_value( in, nnumber, FIELDS_CHRP ), LEVEL_MAIN );
731 		if ( fstatus!=FIELDS_OK ) {
732 			*status = BIBL_ERR_MEMERR;
733 			return;
734 		}
735 	}
736 }
737 
738 static void
append_howpublished(fields * in,fields * out,int * status)739 append_howpublished( fields *in, fields *out, int *status )
740 {
741 	int n, fstatus;
742 	char *d;
743 
744 	n = fields_find( in, "GENRE:BIBUTILS", LEVEL_ANY );
745 	if ( n==FIELDS_NOTFOUND ) return;
746 
747 	d = fields_value( in, n, FIELDS_CHRP_NOUSE );
748 	if ( !strcmp( d, "Habilitation thesis" ) ) {
749 		fstatus = fields_add( out, "howpublised", d, LEVEL_MAIN );
750 		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
751 	}
752 	if ( !strcmp( d, "Licentiate thesis" ) ) {
753 		fstatus = fields_add( out, "howpublised", d, LEVEL_MAIN );
754 		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
755 	}
756 	if ( !strcmp( d, "Diploma thesis" ) ) {
757 		fstatus = fields_add( out, "howpublised", d, LEVEL_MAIN );
758 		if ( fstatus!=FIELDS_OK ) *status = BIBL_ERR_MEMERR;
759 	}
760 }
761 
762 static int
biblatexout_assemble(fields * in,fields * out,param * pm,unsigned long refnum)763 biblatexout_assemble( fields *in, fields *out, param *pm, unsigned long refnum )
764 {
765 	int type, status = BIBL_OK;
766 
767 	type = biblatexout_type( in, pm->progname, "", refnum );
768 
769 	append_type        ( type, out, &status );
770 	append_citekey     ( in, out, pm->format_opts, &status );
771 	append_people      ( in, "AUTHOR",     "AUTHOR:CORP",     "AUTHOR:ASIS",     "author",       LEVEL_MAIN, out, pm->format_opts, pm->latexout, &status );
772 	append_people      ( in, "AUTHOR",     "AUTHOR:CORP",     "AUTHOR:ASIS",     "bookauthor",   LEVEL_HOST, out, pm->format_opts, pm->latexout, &status );
773 	append_people      ( in, "EDITOR",     "EDITOR:CORP",     "EDITOR:ASIS",     "editor",       LEVEL_ANY, out, pm->format_opts, pm->latexout, &status );
774 	append_people      ( in, "ANNOTATOR",  "ANNOTATOR:CORP",  "ANNOTATOR:ASIS",  "annotator",    LEVEL_ANY, out, pm->format_opts, pm->latexout, &status );
775 	append_people      ( in, "TRANSLATOR", "TRANSLATOR:CORP", "TRANSLATOR:ASIS", "translator",   LEVEL_ANY, out, pm->format_opts, pm->latexout, &status );
776 	append_people      ( in, "REDACTOR",   "REDACTOR:CORP",   "REDACTOR:ASIS",   "redactor",     LEVEL_ANY, out, pm->format_opts, pm->latexout, &status );
777 	append_people      ( in, "COMMENTATOR","COMMENTATOR:CORP","COMMENTATOR:ASIS","commentator",  LEVEL_ANY, out, pm->format_opts, pm->latexout, &status );
778 	append_people      ( in, "INTROAUTHOR","INTROAUTHOR:CORP","INTROAUTHOR:ASIS","introduction", LEVEL_ANY, out, pm->format_opts, pm->latexout, &status );
779 	append_people      ( in, "AFTERAUTHOR","AFTERAUTHOR:CORP","AFTERAUTHOR:ASIS","afterword",    LEVEL_ANY, out, pm->format_opts, pm->latexout, &status );
780 	append_titles      ( in, type, out, pm->format_opts, &status );
781 	append_simple      ( in, "SHORTTITLE",         "shorttitle", out, &status );
782 	append_date        ( in, out, &status );
783 	append_simple      ( in, "EDITION",            "edition",   out, &status );
784 	append_simple      ( in, "PUBLISHER",          "publisher", out, &status );
785 	append_simple      ( in, "ADDRESS",            "address",   out, &status );
786 	append_simple      ( in, "EDITION",            "version",   out, &status );
787 	append_simple      ( in, "PART",               "part",      out, &status );
788 	append_simple      ( in, "VOLUME",             "volume",    out, &status );
789 	append_issue_number( in, out, &status );
790 	append_pages       ( in, out, pm->format_opts, &status );
791 	append_keywords    ( in, out, &status );
792 	append_simple      ( in, "LANGCATALOG",        "hyphenation", out, &status );
793 	append_simple      ( in, "CONTENTS",           "contents",    out, &status );
794 	append_simple      ( in, "ABSTRACT",           "abstract",    out, &status );
795 	append_simple      ( in, "LOCATION",           "location",    out, &status );
796 	append_simple      ( in, "DEGREEGRANTOR",      "school",      out, &status );
797 	append_simple      ( in, "DEGREEGRANTOR:ASIS", "school",      out, &status );
798 	append_simple      ( in, "DEGREEGRANTOR:CORP", "school",      out, &status );
799 	append_simpleall   ( in, "NOTES",              "note",        out, &status );
800 	append_simpleall   ( in, "ANNOTE",             "annote",      out, &status );
801 	append_simpleall   ( in, "ANNOTATION",         "annotation",  out, &status );
802 	append_simple      ( in, "ISBN",               "isbn",        out, &status );
803 	append_simple      ( in, "ISSN",               "issn",        out, &status );
804 	append_simple      ( in, "MRNUMBER",           "mrnumber",    out, &status );
805 	append_simple      ( in, "CODEN",              "coden",       out, &status );
806 	append_simple      ( in, "DOI",                "doi",         out, &status );
807 	append_simple      ( in, "EID",                "eid",         out, &status );
808 	append_urls        ( in, out, &status );
809 	append_fileattach  ( in, out, &status );
810 	append_arxiv       ( in, out, &status );
811 	append_simple      ( in, "EPRINTCLASS",        "primaryClass", out, &status );
812 	append_isi         ( in, out, &status );
813 	append_simple      ( in, "LANGUAGE",           "language",  out, &status );
814 	append_howpublished( in, out, &status );
815 
816 	return status;
817 }
818 
819 /*****************************************************
820  PUBLIC: int biblatexout_write()
821 *****************************************************/
822 
823 static int
biblatexout_write(fields * out,FILE * fp,param * pm,unsigned long refnum)824 biblatexout_write( fields *out, FILE *fp, param *pm, unsigned long refnum )
825 {
826 	int i, j, len, nquotes, format_opts = pm->format_opts;
827 	char *tag, *value, ch;
828 
829 	/* ...output type information "@article{" */
830 	value = ( char * ) fields_value( out, 0, FIELDS_CHRP );
831 	if ( !(format_opts & BIBL_FORMAT_BIBOUT_UPPERCASE) ) fprintf( fp, "@%s{", value );
832 	else {
833 		len = (value) ? strlen( value ) : 0;
834 		fprintf( fp, "@" );
835 		for ( i=0; i<len; ++i )
836 			fprintf( fp, "%c", toupper((unsigned char)value[i]) );
837 		fprintf( fp, "{" );
838 	}
839 
840 	/* ...output refnum "Smith2001" */
841 	value = ( char * ) fields_value( out, 1, FIELDS_CHRP );
842 	fprintf( fp, "%s", value );
843 
844 	/* ...rest of the references */
845 	for ( j=2; j<out->n; ++j ) {
846 		nquotes = 0;
847 		tag   = ( char * ) fields_tag( out, j, FIELDS_CHRP );
848 		value = ( char * ) fields_value( out, j, FIELDS_CHRP );
849 		fprintf( fp, ",\n" );
850 		if ( format_opts & BIBL_FORMAT_BIBOUT_WHITESPACE ) fprintf( fp, "  " );
851 		if ( !(format_opts & BIBL_FORMAT_BIBOUT_UPPERCASE ) ) fprintf( fp, "%s", tag );
852 		else {
853 			len = strlen( tag );
854 			for ( i=0; i<len; ++i )
855 				fprintf( fp, "%c", toupper((unsigned char)tag[i]) );
856 		}
857 		if ( format_opts & BIBL_FORMAT_BIBOUT_WHITESPACE ) fprintf( fp, " = \t" );
858 		else fprintf( fp, "=" );
859 
860 		if ( format_opts & BIBL_FORMAT_BIBOUT_BRACKETS ) fprintf( fp, "{" );
861 		else fprintf( fp, "\"" );
862 
863 		len = strlen( value );
864 		for ( i=0; i<len; ++i ) {
865 			ch = value[i];
866 			if ( ch!='\"' ) fprintf( fp, "%c", ch );
867 			else {
868 				if ( format_opts & BIBL_FORMAT_BIBOUT_BRACKETS || ( i>0 && value[i-1]=='\\' ) )
869 					fprintf( fp, "\"" );
870 				else {
871 					if ( nquotes % 2 == 0 )
872 						fprintf( fp, "``" );
873 					else    fprintf( fp, "\'\'" );
874 					nquotes++;
875 				}
876 			}
877 		}
878 
879 		if ( format_opts & BIBL_FORMAT_BIBOUT_BRACKETS ) fprintf( fp, "}" );
880 		else fprintf( fp, "\"" );
881 	}
882 
883 	/* ...finish reference */
884 	if ( format_opts & BIBL_FORMAT_BIBOUT_FINALCOMMA ) fprintf( fp, "," );
885 	fprintf( fp, "\n}\n\n" );
886 
887 	fflush( fp );
888 
889 	return BIBL_OK;
890 }
891