1 /*
2  * bibcore.c
3  *
4  * Copyright (c) Chris Putnam 2005-2020
5  *
6  * Source code released under the GPL version 2
7  *
8  */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include "bibutils.h"
12 
13 /* internal includes */
14 #include "reftypes.h"
15 #include "charsets.h"
16 #include "str_conv.h"
17 #include "is_ws.h"
18 
19 /* illegal modes to pass in, but use internally for consistency */
20 #define BIBL_INTERNALIN   (BIBL_LASTIN+1)
21 #define BIBL_INTERNALOUT  (BIBL_LASTOUT+1)
22 
23 #define debug_set( p ) ( (p)->verbose > 1 )
24 #define verbose_set( p ) ( (p)->verbose )
25 
26 static void
report_params(FILE * fp,const char * f,param * p)27 report_params( FILE *fp, const char *f, param *p )
28 {
29 	fflush( NULL );
30 
31 	fprintf( fp, "-------------------params start for %s\n", f );
32 	fprintf( fp, "\tprogname='%s'\n\n", p->progname );
33 
34 	fprintf( fp, "\treadformat=%d", p->readformat );
35 	switch ( p->readformat ) {
36 		case BIBL_INTERNALIN:   fprintf( fp, " (BIBL_INTERNALIN)\n" );   break;
37 		case BIBL_MODSIN:       fprintf( fp, " (BIBL_MODSIN)\n" );       break;
38 		case BIBL_BIBTEXIN:     fprintf( fp, " (BIBL_BIBTEXIN)\n" );     break;
39 		case BIBL_RISIN:        fprintf( fp, " (BIBL_RISIN)\n" );        break;
40 		case BIBL_ENDNOTEIN:    fprintf( fp, " (BIBL_ENDNOTEIN)\n" );    break;
41 		case BIBL_COPACIN:      fprintf( fp, " (BIBL_COPACIN)\n" );      break;
42 		case BIBL_ISIIN:        fprintf( fp, " (BIBL_ISIIN)\n" );        break;
43 		case BIBL_MEDLINEIN:    fprintf( fp, " (BIBL_MEDLINEIN)\n" );    break;
44 		case BIBL_ENDNOTEXMLIN: fprintf( fp, " (BIBL_ENDNOTEXMLIN)\n" ); break;
45 		case BIBL_BIBLATEXIN:   fprintf( fp, " (BIBL_BIBLATEXIN)\n" );   break;
46 		case BIBL_EBIIN:        fprintf( fp, " (BIBL_EBIIN)\n" );        break;
47 		case BIBL_WORDIN:       fprintf( fp, " (BIBL_WORDIN)\n" );       break;
48 		case BIBL_NBIBIN:       fprintf( fp, " (BIBL_NBIBIN)\n" );       break;
49 		default:                fprintf( fp, " (Illegal value)\n" );     break;
50 	}
51 	fprintf( fp, "\tcharsetin=%d\n", p->charsetin );
52 	fprintf( fp, "\tcharsetin_src=%d", p->charsetin_src );
53 	switch ( p->charsetin_src ) {
54 		case BIBL_SRC_DEFAULT:  fprintf( fp, " (BIBL_SRC_DEFAULT)\n" ); break;
55 		case BIBL_SRC_FILE:     fprintf( fp, " (BIBL_SRC_FILE)\n" );    break;
56 		case BIBL_SRC_USER:     fprintf( fp, " (BIBL_SRC_USER)\n" );    break;
57 		default:                fprintf( fp, " (Illegal value)\n" );    break;
58 	}
59 	fprintf( fp, "\tutf8in=%d\n", p->utf8in );
60 	fprintf( fp, "\tlatexin=%d\n", p->latexin );
61 	fprintf( fp, "\txmlin=%d\n\n", p->xmlin );
62 
63 	fprintf( fp, "\twriteformat=%d", p->writeformat );
64 	switch ( p->writeformat ) {
65 		case BIBL_INTERNALOUT:  fprintf( fp, " (BIBL_INTERNALOUT)\n" );  break;
66 		case BIBL_ADSABSOUT:    fprintf( fp, " (BIBL_ADSABSOUT)\n" );    break;
67 		case BIBL_BIBTEXOUT:    fprintf( fp, " (BIBL_BIBTEXOUT)\n" );    break;
68 		case BIBL_ENDNOTEOUT:   fprintf( fp, " (BIBL_ENDNOTEOUT)\n" );   break;
69 		case BIBL_ISIOUT:       fprintf( fp, " (BIBL_ISIOUT)\n" );       break;
70 		case BIBL_MODSOUT:      fprintf( fp, " (BIBL_MODSOUT)\n" );      break;
71 		case BIBL_NBIBOUT:      fprintf( fp, " (BIBL_NBIBOUT)\n" );      break;
72 		case BIBL_RISOUT:       fprintf( fp, " (BIBL_RISOUT)\n" );       break;
73 		case BIBL_WORD2007OUT:  fprintf( fp, " (BIBL_WORD2007OUT)\n" );  break;
74 		default:                fprintf( fp, " (Illegal value)\n");      break;
75 	}
76 	fprintf( fp, "\tcharsetout=%d\n", p->charsetout );
77 	fprintf( fp, "\tcharsetout_src=%d", p->charsetout_src );
78 	switch ( p->charsetout_src ) {
79 		case BIBL_SRC_DEFAULT:  fprintf( fp, " (BIBL_SRC_DEFAULT)\n" ); break;
80 		case BIBL_SRC_FILE:     fprintf( fp, " (BIBL_SRC_FILE)\n" );    break;
81 		case BIBL_SRC_USER:     fprintf( fp, " (BIBL_SRC_USER)\n" );    break;
82 		default:                fprintf( fp, " (Illegal value)\n" );    break;
83 	}
84 	fprintf( fp, "\tutf8out=%d\n", p->utf8out );
85 	fprintf( fp, "\tutf8bom=%d\n", p->utf8bom );
86 	fprintf( fp, "\tlatexout=%d\n", p->latexout );
87 	fprintf( fp, "\txmlout=%d\n", p->xmlout );
88 	fprintf( fp, "-------------------params end for %s\n", f );
89 
90 	fflush( fp );
91 }
92 
93 /* bibl_duplicateparams()
94  *
95  * Returns status of BIBL_OK or BIBL_ERR_MEMERR
96  */
97 static int
bibl_duplicateparams(param * np,param * op)98 bibl_duplicateparams( param *np, param *op )
99 {
100 	int status;
101 
102 	slist_init( &(np->asis) );
103 	status = slist_copy( &(np->asis), &(op->asis ) );
104 	if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
105 
106 	slist_init( &(np->corps) );
107 	status = slist_copy( &(np->corps), &(op->corps ) );
108 	if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
109 
110 	if ( !op->progname ) np->progname = NULL;
111 	else {
112 		np->progname = strdup( op->progname );
113 		if ( !np->progname ) return BIBL_ERR_MEMERR;
114 	}
115 
116 	np->readformat    = op->readformat;
117 	np->charsetin     = op->charsetin;
118 	np->charsetin_src = op->charsetin_src;
119 	np->utf8in        = op->utf8in;
120 	np->latexin       = op->latexin;
121 	np->xmlin         = op->xmlin;
122 
123 	np->writeformat    = op->writeformat;
124 	np->charsetout     = op->charsetout;
125 	np->charsetout_src = op->charsetout_src;
126 	np->utf8out        = op->utf8out;
127 	np->utf8bom        = op->utf8bom;
128 	np->latexout       = op->latexout;
129 	np->xmlout         = op->xmlout;
130 	np->nosplittitle   = op->nosplittitle;
131 
132 	np->verbose          = op->verbose;
133 	np->format_opts      = op->format_opts;
134 	np->addcount         = op->addcount;
135 	np->output_raw       = op->output_raw;
136 	np->singlerefperfile = op->singlerefperfile;
137 
138 	np->readf     = op->readf;
139 	np->processf  = op->processf;
140 	np->cleanf    = op->cleanf;
141 	np->typef     = op->typef;
142 	np->convertf  = op->convertf;
143 	np->headerf   = op->headerf;
144 	np->footerf   = op->footerf;
145 	np->assemblef = op->assemblef;
146 	np->writef    = op->writef;
147 
148 	np->all       = op->all;
149 	np->nall      = op->nall;
150 
151 	return BIBL_OK;
152 }
153 
154 /* bibl_setreadparams()
155  *
156  * Returns status of BIBL_OK or BIBL_ERR_MEMERR
157  */
158 static int
bibl_setreadparams(param * np,param * op)159 bibl_setreadparams( param *np, param *op )
160 {
161 	int status;
162 	status = bibl_duplicateparams( np, op );
163 	if ( status == BIBL_OK ) {
164 		np->utf8out        = 1;
165 		np->charsetout     = BIBL_CHARSET_UNICODE;
166 		np->charsetout_src = BIBL_SRC_DEFAULT;
167 		np->xmlout         = BIBL_XMLOUT_FALSE;
168 		np->latexout       = 0;
169 		np->writeformat    = BIBL_INTERNALOUT;
170 	}
171 	return status;
172 }
173 
174 /* bibl_setwriteparams()
175  *
176  * Returns status of BIBL_OK or BIBL_ERR_MEMERR
177  */
178 static int
bibl_setwriteparams(param * np,param * op)179 bibl_setwriteparams( param *np, param *op )
180 {
181 	int status;
182 	status = bibl_duplicateparams( np, op );
183 	if ( status == BIBL_OK ) {
184 		np->xmlin         = 0;
185 		np->latexin       = 0;
186 		np->utf8in        = 1;
187 		np->charsetin     = BIBL_CHARSET_UNICODE;
188 		np->charsetin_src = BIBL_SRC_DEFAULT;
189 		np->readformat    = BIBL_INTERNALIN;
190 	}
191 	return status;
192 }
193 
194 void
bibl_freeparams(param * p)195 bibl_freeparams( param *p )
196 {
197 	if ( p ) {
198 		slist_free( &(p->asis) );
199 		slist_free( &(p->corps) );
200 		if ( p->progname ) free( p->progname );
201 	}
202 }
203 
204 int
bibl_readasis(param * p,char * f)205 bibl_readasis( param *p, char *f )
206 {
207 	int status;
208 
209 	if ( !p ) return BIBL_ERR_BADINPUT;
210 	if ( !f ) return BIBL_ERR_BADINPUT;
211 
212 	status = slist_fill( &(p->asis), f, 1 );
213 
214 	if ( status == SLIST_ERR_CANTOPEN ) return BIBL_ERR_CANTOPEN;
215 	else if ( status == SLIST_ERR_MEMERR ) return BIBL_ERR_MEMERR;
216 	return BIBL_OK;
217 }
218 
219 int
bibl_readcorps(param * p,char * f)220 bibl_readcorps( param *p, char *f )
221 {
222 	int status;
223 
224 	if ( !p ) return BIBL_ERR_BADINPUT;
225 	if ( !f ) return BIBL_ERR_BADINPUT;
226 
227 	status = slist_fill( &(p->corps), f, 1 );
228 
229 	if ( status == SLIST_ERR_CANTOPEN ) return BIBL_ERR_CANTOPEN;
230 	else if ( status == 0 ) return BIBL_ERR_MEMERR;
231 	return BIBL_OK;
232 }
233 
234 /* bibl_addtoasis()
235  *
236  * Returns BIBL_OK or BIBL_ERR_MEMERR
237  */
238 int
bibl_addtoasis(param * p,char * d)239 bibl_addtoasis( param *p, char *d )
240 {
241 	int status;
242 
243 	if ( !p ) return BIBL_ERR_BADINPUT;
244 	if ( !d ) return BIBL_ERR_BADINPUT;
245 
246 	status = slist_addc( &(p->asis), d );
247 
248 	return ( status==SLIST_OK )? BIBL_OK : BIBL_ERR_MEMERR;
249 }
250 
251 /* bibl_addtocorps()
252  *
253  * Returns BIBL_OK or BIBL_ERR_MEMERR
254  */
255 int
bibl_addtocorps(param * p,char * d)256 bibl_addtocorps( param *p, char *d )
257 {
258 	int status;
259 
260 	if ( !p ) return BIBL_ERR_BADINPUT;
261 	if ( !d ) return BIBL_ERR_BADINPUT;
262 
263 	status = slist_addc( &(p->corps), d );
264 
265 	return ( status==SLIST_OK )? BIBL_OK : BIBL_ERR_MEMERR;
266 }
267 
268 void
bibl_reporterr(int err)269 bibl_reporterr( int err )
270 {
271 	// Patch: Disable output logging
272 }
273 
274 static int
bibl_illegalinmode(int mode)275 bibl_illegalinmode( int mode )
276 {
277 	if ( mode < BIBL_FIRSTIN || mode > BIBL_LASTIN ) return 1;
278 	else return 0;
279 }
280 
281 static int
bibl_illegaloutmode(int mode)282 bibl_illegaloutmode( int mode )
283 {
284 	if ( mode < BIBL_FIRSTOUT || mode > BIBL_LASTOUT ) return 1;
285 	else return 0;
286 }
287 
288 static void
bibl_verbose_reference(fields * f,char * filename,long refnum)289 bibl_verbose_reference( fields *f, char *filename, long refnum )
290 {
291 	// Patch: Disable output logging
292 }
293 
294 static void
bibl_verbose(bibl * bin,const char * msg1,const char * msg2)295 bibl_verbose( bibl *bin, const char *msg1, const char *msg2 )
296 {
297 	// Patch: Disable output logging
298 }
299 
300 
301 /* extract_tag_value
302  *
303  * Extract the tag and the value for ALWAYS/DEFAULT
304  * entries like: "GENRE:BIBUTILS|Masters thesis"
305  *
306  * tag = "GENRE:BIBUTILS"
307  * value = "Masters thesis"
308  */
309 static int
extract_tag_value(str * tag,str * value,char * p)310 extract_tag_value( str *tag, str *value, char *p )
311 {
312 	str_empty( tag );
313 	while ( p && *p && *p!='|' ) {
314 		str_addchar( tag, *p );
315 		p++;
316 	}
317 	if ( str_memerr( tag ) ) return BIBL_ERR_MEMERR;
318 
319 	if ( p && *p=='|' ) p++;
320 
321 	str_empty( value );
322 	while ( p && *p ) {
323 		str_addchar( value, *p );
324 		p++;
325 	}
326 	if ( str_memerr( tag ) ) return BIBL_ERR_MEMERR;
327 
328 	return BIBL_OK;
329 }
330 
331 /* process_defaultadd()
332  *
333  * Add tag/value pairs that have "DEFAULT" processing
334  * unless a tag/value pair with the same tag has already
335  * been adding during reference processing.
336  */
337 static int
process_defaultadd(fields * f,int reftype,param * r)338 process_defaultadd( fields *f, int reftype, param *r )
339 {
340 	int i, n, process, level, status, ret = BIBL_OK;
341 	str tag, value;
342 	char *p;
343 
344 	strs_init( &tag, &value, NULL );
345 
346 	for ( i=0; i<r->all[reftype].ntags; ++i ) {
347 
348 		process = ((r->all[reftype]).tags[i]).processingtype;
349 		if ( process!=DEFAULT ) continue;
350 
351 		level   = ((r->all[reftype]).tags[i]).level;
352 		p       = ((r->all[reftype]).tags[i]).newstr;
353 
354 		status = extract_tag_value( &tag, &value, p );
355 		if ( status!=BIBL_OK ) {
356 			ret = status;
357 			goto out;
358 		}
359 
360 		n = fields_find( f, tag.data, level );
361 		if ( n==FIELDS_NOTFOUND ) {
362 			status = fields_add( f, tag.data, value.data, level );
363 			if ( status!=FIELDS_OK ) {
364 				ret = BIBL_ERR_MEMERR;
365 				goto out;
366 			}
367 		}
368 
369 	}
370 out:
371 	strs_free( &tag, &value, NULL );
372 
373 	return ret;
374 }
375 
376 /* process_alwaysadd()
377  *
378  * Add tag/value pair to reference from the ALWAYS
379  * processing type without exception (the difference from
380  * DEFAULT processing).
381  */
382 static int
process_alwaysadd(fields * f,int reftype,param * r)383 process_alwaysadd( fields *f, int reftype, param *r )
384 {
385 	int i, process, level, status, ret = BIBL_OK;
386 	str tag, value;
387 	char *p;
388 
389 	strs_init( &tag, &value, NULL );
390 
391 	for ( i=0; i<r->all[reftype].ntags; ++i ) {
392 
393 		process = ((r->all[reftype]).tags[i]).processingtype;
394 		if ( process!=ALWAYS ) continue;
395 
396 		level   = ((r->all[reftype]).tags[i]).level;
397 		p       = ((r->all[reftype]).tags[i]).newstr;
398 
399 		status = extract_tag_value( &tag, &value, p );
400 		if ( status!=BIBL_OK ) {
401 			ret = status;
402 			goto out;
403 		}
404 
405 		status = fields_add( f, tag.data, value.data, level );
406 		if ( status!=FIELDS_OK ) {
407 			ret = BIBL_ERR_MEMERR;
408 			goto out;
409 		}
410 	}
411 
412 out:
413 	strs_free( &tag, &value, NULL );
414 
415 	return ret;
416 }
417 
418 static int
read_refs(FILE * fp,bibl * bin,char * filename,param * p)419 read_refs( FILE *fp, bibl *bin, char *filename, param *p )
420 {
421 	int refnum = 0, bufpos = 0, ret=BIBL_OK, fcharset;/* = CHARSET_UNKNOWN;*/
422 	str reference, line;
423 	char buf[256]="";
424 	fields *ref;
425 
426 	str_init( &reference );
427 	str_init( &line );
428 	while ( p->readf( fp, buf, sizeof(buf), &bufpos, &line, &reference, &fcharset ) ) {
429 		if ( reference.len==0 ) continue;
430 		ref = fields_new();
431 		if ( !ref ) {
432 			ret = BIBL_ERR_MEMERR;
433 			bibl_free( bin );
434 			goto out;
435 		}
436 		if ( p->processf( ref, reference.data, filename, refnum+1, p )){
437 			ret = bibl_addref( bin, ref );
438 			if ( ret!=BIBL_OK ) {
439 				bibl_free( bin );
440 				fields_delete( ref );
441 				goto out;
442 			}
443 			refnum += 1;
444 		} else {
445 			fields_delete( ref );
446 		}
447 		str_empty( &reference );
448 		if ( fcharset!=CHARSET_UNKNOWN ) {
449 			/* charset from file takes priority over default, but
450 			 * not user-specified */
451 			if ( p->charsetin_src!=BIBL_SRC_USER ) {
452 				p->charsetin_src = BIBL_SRC_FILE;
453 				p->charsetin = fcharset;
454 				if ( fcharset!=CHARSET_UNICODE ) p->utf8in = 0;
455 			}
456 		}
457 	}
458 	if ( p->charsetin==CHARSET_UNICODE ) p->utf8in = 1;
459 out:
460 	str_free( &line );
461 	str_free( &reference );
462 	return ret;
463 }
464 
465 /* Don't manipulate latex for URL's and the like */
466 static int
bibl_notexify(char * tag)467 bibl_notexify( char *tag )
468 {
469 	char *protected[] = { "DOI", "URL", "REFNUM", "FILEATTACH", "FILE" };
470 	int i, nprotected = sizeof( protected ) / sizeof( protected[0] );
471 	for ( i=0; i<nprotected; ++i )
472 		if ( !strcasecmp( tag, protected[i] ) ) return 1;
473 	return 0;
474 }
475 
476 /* bibl_fixcharsetdata()
477  *
478  * returns BIBL_OK or BIBL_ERR_MEMERR
479  */
480 static int
bibl_fixcharsetdata(fields * ref,param * p)481 bibl_fixcharsetdata( fields *ref, param *p )
482 {
483 	str *data;
484 	char *tag;
485 	long i, n;
486 	int ok;
487 
488 	n = fields_num( ref );
489 
490 	for ( i=0; i<n; ++i ) {
491 
492 		tag  = fields_tag( ref, i, FIELDS_CHRP_NOUSE );
493 		data = fields_value( ref, i, FIELDS_STRP_NOUSE );
494 
495 		if ( bibl_notexify( tag ) ) {
496 			ok = str_convert( data,
497 				p->charsetin,  0, p->utf8in,  p->xmlin,
498 				p->charsetout, 0, p->utf8out, p->xmlout );
499 		} else {
500 			ok = str_convert( data,
501 				p->charsetin,  p->latexin,  p->utf8in,  p->xmlin,
502 				p->charsetout, p->latexout, p->utf8out, p->xmlout );
503 		}
504 
505 		if ( !ok ) return BIBL_ERR_MEMERR;
506 	}
507 
508 	return BIBL_OK;
509 }
510 
511 /* bibl_fixcharsets()
512  *
513  * returns BIBL_OK or BIBL_ERR_MEMERR
514  */
515 static int
bibl_fixcharsets(bibl * b,param * p)516 bibl_fixcharsets( bibl *b, param *p )
517 {
518 	int status;
519 	long i;
520 
521 	for ( i=0; i<b->n; ++i ) {
522 		status = bibl_fixcharsetdata( b->ref[i], p );
523 		if ( status!=BIBL_OK ) return status;
524 	}
525 
526 	return BIBL_OK;
527 }
528 
529 static int
bibl_addcount(bibl * b)530 bibl_addcount( bibl *b )
531 {
532 	char buf[512];
533 	fields *ref;
534 	long i;
535 	int n;
536 
537 	for ( i=0; i<b->n; ++i ) {
538 
539 		ref = b->ref[i];
540 
541 		n = fields_find( ref, "REFNUM", LEVEL_MAIN );
542 		if ( n==FIELDS_NOTFOUND ) continue;
543 
544 		sprintf( buf, "_%ld", i+1 );
545 		str_strcatc( fields_value( ref, n, FIELDS_STRP_NOUSE ), buf );
546 		if ( str_memerr( fields_value( ref, n, FIELDS_STRP_NOUSE ) ) ) {
547 			return BIBL_ERR_MEMERR;
548 		}
549 
550 	}
551 
552 	return BIBL_OK;
553 }
554 
555 static int
generate_citekey(fields * f,long nref)556 generate_citekey( fields *f, long nref )
557 {
558 	int n1, n2, status, ret;
559 	char *p, buf[100];
560 	str citekey;
561 
562 	str_init( &citekey );
563 
564 	n1 = fields_find( f, "AUTHOR", LEVEL_MAIN );
565 	if ( n1==FIELDS_NOTFOUND ) n1 = fields_find( f, "AUTHOR:ASIS", LEVEL_MAIN );
566 	if ( n1==FIELDS_NOTFOUND ) n1 = fields_find( f, "AUTHOR:CORP", LEVEL_MAIN );
567 	if ( n1==FIELDS_NOTFOUND ) n1 = fields_find( f, "AUTHOR", LEVEL_ANY );
568 	if ( n1==FIELDS_NOTFOUND ) n1 = fields_find( f, "AUTHOR:ASIS", LEVEL_ANY );
569 	if ( n1==FIELDS_NOTFOUND ) n1 = fields_find( f, "AUTHOR:CORP", LEVEL_ANY );
570 
571 	n2 = fields_find( f, "DATE:YEAR", LEVEL_MAIN );
572 	if ( n2==FIELDS_NOTFOUND ) n2 = fields_find( f, "DATE:YEAR", LEVEL_ANY );
573 	if ( n2==FIELDS_NOTFOUND ) n2 = fields_find( f, "PARTDATE:YEAR", LEVEL_MAIN );
574 	if ( n2==FIELDS_NOTFOUND ) n2 = fields_find( f, "PARTDATE:YEAR", LEVEL_ANY );
575 
576 	if ( n1!=FIELDS_NOTFOUND && n2!=FIELDS_NOTFOUND ) {
577 
578 		p = fields_value( f, n1, FIELDS_CHRP_NOUSE );
579 		while ( p && *p && *p!='|' ) {
580 			if ( !is_ws( *p ) ) str_addchar( &citekey, *p );
581 			p++;
582 		}
583 
584 		p = fields_value( f, n2, FIELDS_CHRP_NOUSE );
585 		while ( p && *p ) {
586 			if ( !is_ws( *p ) ) str_addchar( &citekey, *p );
587 			p++;
588 		}
589 
590 	}
591 
592 	else {
593 		sprintf( buf, "ref%ld", nref );
594 		str_strcpyc( &citekey, buf );
595 	}
596 
597 	if ( str_memerr( &citekey ) ) {
598 		ret = -1;
599 		goto out;
600 	}
601 
602 	status = fields_add( f, "REFNUM", str_cstr( &citekey ), LEVEL_MAIN );
603 	if ( status!=FIELDS_OK ) {
604 		ret = -1;
605 		goto out;
606 	}
607 
608 	ret = fields_find( f, "REFNUM", LEVEL_MAIN );
609 out:
610 	str_free( &citekey );
611 	return ret;
612 }
613 
614 static int
get_citekeys(bibl * bin,slist * citekeys)615 get_citekeys( bibl *bin, slist *citekeys )
616 {
617 	int n, status;
618 	fields *f;
619 	long i;
620 
621 	for ( i=0; i<bin->n; ++i ) {
622 		f = bin->ref[i];
623 		n = fields_find( f, "REFNUM", LEVEL_ANY );
624 		if ( n==FIELDS_NOTFOUND ) n = generate_citekey( f, i+1 );
625 		if ( n!=FIELDS_NOTFOUND && fields_has_value( f, n ) ) {
626 			status = slist_add( citekeys, fields_value( f, n, FIELDS_STRP_NOUSE ) );
627 			if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
628 		} else {
629 			status = slist_addc( citekeys, "" );
630 			if ( status!=SLIST_OK ) return BIBL_ERR_MEMERR;
631 		}
632 	}
633 
634 	return BIBL_OK;
635 }
636 
637 static int
identify_duplicates(bibl * b,slist * citekeys,int * dup)638 identify_duplicates( bibl *b, slist *citekeys, int *dup )
639 {
640 	int i, j, ndup = 0;
641 
642 	for ( i=0; i<citekeys->n-1; ++i ) {
643 		if ( dup[i]!=-1 ) continue;
644 		for ( j=i+1; j<citekeys->n; ++j ) {
645 			if ( !strcmp( slist_cstr( citekeys, i ),
646 			              slist_cstr( citekeys, j ) ) ) {
647 					dup[i] = i;
648 					dup[j] = i;
649 					ndup++;
650 			}
651 		}
652 	}
653 
654 	return ndup;
655 }
656 
657 static int
build_new_citekey(int nsame,str * old_citekey,str * new_citekey)658 build_new_citekey( int nsame, str *old_citekey, str *new_citekey )
659 {
660 	const char abc[]="abcdefghijklmnopqrstuvwxyz";
661 
662 	str_strcpy( new_citekey, old_citekey );
663 
664 	while ( nsame >= 26 ) {
665 		str_addchar( new_citekey, 'a' );
666 		nsame -= 26;
667 	}
668 
669 	if ( nsame>=0 ) str_addchar( new_citekey, abc[nsame] );
670 
671 	return ( str_memerr( new_citekey ) ) ? BIBL_ERR_MEMERR : BIBL_OK;
672 }
673 
674 static int
resolve_duplicates(bibl * b,slist * citekeys,int * dup)675 resolve_duplicates( bibl *b, slist *citekeys, int *dup )
676 {
677 	int nsame, n, i, j, status = BIBL_OK;
678 	str new_citekey, *ref_citekey;
679 
680 	str_init( &new_citekey );
681 
682 	for ( i=0; i<citekeys->n; ++i ) {
683 
684 		if ( dup[i]==-1 ) continue;
685 
686 		nsame = 0;
687 
688 		for ( j=i; j<citekeys->n; ++j ) {
689 
690 			if ( dup[j]!=i ) continue;
691 
692 			dup[j] = -1;
693 
694 			status = build_new_citekey( nsame, slist_str( citekeys, j ), &new_citekey );
695 			if ( status!=BIBL_OK ) goto out;
696 
697 			n = fields_find( b->ref[j], "REFNUM", LEVEL_ANY );
698 			if ( n==FIELDS_NOTFOUND ) continue;
699 
700 			ref_citekey = fields_value( b->ref[j], n, FIELDS_STRP_NOUSE );
701 
702 			str_strcpy( ref_citekey, &new_citekey );
703 			if ( str_memerr( ref_citekey ) ) { status = BIBL_ERR_MEMERR; goto out; }
704 
705 			nsame++;
706 		}
707 	}
708 out:
709 	str_free( &new_citekey );
710 	return status;
711 }
712 
713 static int
identify_and_resolve_duplicate_citekeys(bibl * b,slist * citekeys)714 identify_and_resolve_duplicate_citekeys( bibl *b, slist *citekeys )
715 {
716 	int i, *dup, ndup, status=BIBL_OK;
717 
718 	dup = ( int * ) malloc( sizeof( int ) * citekeys->n );
719 	if ( !dup ) return BIBL_ERR_MEMERR;
720 	for ( i=0; i<citekeys->n; ++i ) dup[i] = -1;
721 
722 	ndup = identify_duplicates( b, citekeys, dup );
723 
724 	if ( ndup ) status = resolve_duplicates( b, citekeys, dup );
725 
726 	free( dup );
727 	return status;
728 }
729 
730 static int
uniqueify_citekeys(bibl * bin)731 uniqueify_citekeys( bibl *bin )
732 {
733 	slist citekeys;
734 	int status;
735 
736 	slist_init( &citekeys );
737 
738 	status = get_citekeys( bin, &citekeys );
739 	if ( status!=BIBL_OK ) goto out;
740 
741 	status = identify_and_resolve_duplicate_citekeys( bin, &citekeys );
742 out:
743 	slist_free( &citekeys );
744 	return status;
745 }
746 
747 static int
clean_refs(bibl * bin,param * p)748 clean_refs( bibl *bin, param *p )
749 {
750 	if ( p->cleanf ) return p->cleanf( bin, p );
751 	else return BIBL_OK;
752 }
753 
754 static int
convert_refs(bibl * bin,char * fname,bibl * bout,param * p)755 convert_refs( bibl *bin, char *fname, bibl *bout, param *p )
756 {
757 	int reftype = 0, status;
758 	fields *rin, *rout;
759 	long i;
760 
761 	for ( i=0; i<bin->n; ++i ) {
762 
763 		rin = bin->ref[i];
764 
765 		rout = fields_new();
766 		if ( !rout ) return BIBL_ERR_MEMERR;
767 
768 		if ( p->typef ) reftype = p->typef( rin, fname, i+1, p );
769 
770 		status = p->convertf( rin, rout, reftype, p );
771 		if ( status!=BIBL_OK ) return status;
772 
773 		if ( p->all ) {
774 			status = process_alwaysadd( rout, reftype, p );
775 			if ( status!=BIBL_OK ) return status;
776 			status = process_defaultadd( rout, reftype, p );
777 			if ( status!=BIBL_OK ) return status;
778 		}
779 
780 		status = bibl_addref( bout, rout );
781 		if ( status!=BIBL_OK ) return status;
782 	}
783 
784 	return BIBL_OK;
785 }
786 
787 int
bibl_read(bibl * b,FILE * fp,char * filename,param * p)788 bibl_read( bibl *b, FILE *fp, char *filename, param *p )
789 {
790 	// Patch: Disable output logging
791 	int status = BIBL_OK;
792 	param read_params;
793 	bibl bin;
794 
795 	if ( !b )  return BIBL_ERR_BADINPUT;
796 	if ( !fp ) return BIBL_ERR_BADINPUT;
797 	if ( !p )  return BIBL_ERR_BADINPUT;
798 
799 	if ( bibl_illegalinmode( p->readformat ) ) {
800 		return BIBL_ERR_BADINPUT;
801 	}
802 
803 	status = bibl_setreadparams( &read_params, p );
804 	if ( status!=BIBL_OK ) {
805 		return status;
806 	}
807 
808 	bibl_init( &bin );
809 
810 	status = read_refs( fp, &bin, filename, &read_params );
811 	if ( status!=BIBL_OK ) {
812 		bibl_freeparams( &read_params );
813 		return status;
814 	}
815 
816 	if ( debug_set( &read_params ) ) {
817 		bibl_verbose( &bin, "raw_input", "for bibl_read" );
818 	}
819 
820 	if ( !read_params.output_raw ) {
821 		status = clean_refs( &bin, &read_params );
822 		if ( status!=BIBL_OK ) goto out;
823 		if ( debug_set( &read_params ) ) bibl_verbose( &bin, "post_clean_refs", "for bibl_read" );
824 	}
825 
826 	if ( ( !read_params.output_raw ) || ( read_params.output_raw & BIBL_RAW_WITHCHARCONVERT ) ) {
827 		status = bibl_fixcharsets( &bin, &read_params );
828 		if ( status!=BIBL_OK ) goto out;
829 		if ( debug_set( &read_params ) ) bibl_verbose( &bin, "post_fixcharsets", "for bibl_read" );
830 	}
831 
832 	if ( !read_params.output_raw ) {
833 		status = convert_refs( &bin, filename, b, &read_params );
834 		if ( status!=BIBL_OK ) goto out;
835 		if ( debug_set( &read_params ) ) bibl_verbose( b, "post_convert_refs", "for bibl_read" );
836 	}
837 
838 	else {
839 		status = bibl_copy( b, &bin );
840 		if ( status!=BIBL_OK ) goto out;
841 		if ( debug_set( &read_params ) ) bibl_verbose( b, "post_bibl_copy", "for bibl_read" );
842 	}
843 
844 	if ( ( !read_params.output_raw ) || ( read_params.output_raw & BIBL_RAW_WITHMAKEREFID ) ) {
845 		status = uniqueify_citekeys( b );
846 		if ( status!=BIBL_OK ) goto out;
847 		if ( read_params.addcount ) {
848 			status = bibl_addcount( b );
849 			if ( status!=BIBL_OK ) goto out;
850 		}
851 		if ( debug_set( &read_params ) ) bibl_verbose( &bin, "post_uniqueify_citekeys", "for bibl_read" );
852 	}
853 
854 out:
855 	bibl_free( &bin );
856 	bibl_freeparams( &read_params );
857 
858 	return status;
859 }
860 
861 static FILE *
singlerefname(fields * reffields,long nref,int mode)862 singlerefname( fields *reffields, long nref, int mode )
863 {
864 	char outfile[2048];
865 	char suffix[5] = "xml";
866 	FILE *fp;
867 	long count;
868 	int  found;
869 	if      ( mode==BIBL_ADSABSOUT )     strcpy( suffix, "ads" );
870 	else if ( mode==BIBL_BIBTEXOUT )     strcpy( suffix, "bib" );
871 	else if ( mode==BIBL_ENDNOTEOUT )    strcpy( suffix, "end" );
872 	else if ( mode==BIBL_ISIOUT )        strcpy( suffix, "isi" );
873 	else if ( mode==BIBL_MODSOUT )       strcpy( suffix, "xml" );
874 	else if ( mode==BIBL_RISOUT )        strcpy( suffix, "ris" );
875 	else if ( mode==BIBL_WORD2007OUT )   strcpy( suffix, "xml" );
876 	found = fields_find( reffields, "REFNUM", LEVEL_MAIN );
877 	/* find new filename based on reference */
878 	if ( found!=-1 ) {
879 		sprintf( outfile,"%s.%s",(char*)fields_value(reffields,found,FIELDS_CHRP_NOUSE), suffix );
880 	} else  sprintf( outfile,"%ld.%s",nref, suffix );
881 	count = 0;
882 	fp = fopen( outfile, "r" );
883 	while ( fp ) {
884 		fclose(fp);
885 		count++;
886 		if ( count==60000 ) return NULL;
887 		if ( found!=-1 )
888 			sprintf( outfile, "%s_%ld.%s", (char*)fields_value( reffields, found, FIELDS_CHRP_NOUSE ), count, suffix );
889 		else sprintf( outfile,"%ld_%ld.%s", nref, count, suffix );
890 		fp = fopen( outfile, "r" );
891 	}
892 	return fopen( outfile, "w" );
893 }
894 
895 static int
bibl_writeeachfp(FILE * fp,bibl * b,param * p)896 bibl_writeeachfp( FILE *fp, bibl *b, param *p )
897 {
898 	fields out, *use = &out;
899 	int status;
900 	long i;
901 
902 	fields_init( &out );
903 
904 	for ( i=0; i<b->n; ++i ) {
905 
906 		fp = singlerefname( b->ref[i], i, p->writeformat );
907 		if ( !fp ) return BIBL_ERR_CANTOPEN;
908 
909 		if ( p->headerf ) p->headerf( fp, p );
910 
911 		if ( p->assemblef ) {
912 			fields_free( &out );
913 			status = p->assemblef( b->ref[i], &out, p, i );
914 			if ( status!=BIBL_OK ) break;
915 		} else {
916 			use = b->ref[i];
917 		}
918 
919 		status = p->writef( use, fp, p, i );
920 
921 		if ( p->footerf ) p->footerf( fp );
922 		fclose( fp );
923 
924 		if ( status!=BIBL_OK ) return status;
925 	}
926 
927 	return BIBL_OK;
928 }
929 
930 static int
bibl_writefp(FILE * fp,bibl * b,param * p)931 bibl_writefp( FILE *fp, bibl *b, param *p )
932 {
933 	// Patch: Disable output logging
934 	int status = BIBL_OK;
935 	fields out, *use = &out;
936 	long i;
937 
938 	fields_init( &out );
939 
940 	if ( p->headerf ) p->headerf( fp, p );
941 	for ( i=0; i<b->n; ++i ) {
942 
943 		if ( p->assemblef ) {
944 			fields_free( &out );
945 			status = p->assemblef( b->ref[i], &out, p, i );
946 			if ( status!=BIBL_OK ) break;
947 			if ( debug_set( p ) ) bibl_verbose_reference( &out, "", i+1 );
948 		} else {
949 			use = b->ref[i];
950 		}
951 
952 		status = p->writef( use, fp, p, i );
953 		if ( status!=BIBL_OK ) break;
954 
955 	}
956 
957 	if ( p->footerf ) p->footerf( fp );
958 	return status;
959 }
960 
961 int
bibl_write(bibl * b,FILE * fp,param * p)962 bibl_write( bibl *b, FILE *fp, param *p )
963 {
964 	// Patch: Disable output logging
965 	int status;
966 	param lp;
967 
968 	if ( !b ) return BIBL_ERR_BADINPUT;
969 	if ( !p ) return BIBL_ERR_BADINPUT;
970 	if ( bibl_illegaloutmode( p->writeformat ) ) return BIBL_ERR_BADINPUT;
971 	if ( !fp && !p->singlerefperfile ) return BIBL_ERR_BADINPUT;
972 
973 	status = bibl_setwriteparams( &lp, p );
974 	if ( status!=BIBL_OK ) return status;
975 
976 	if ( debug_set( p ) ) bibl_verbose( b, "raw_input", "for bibl_write" );
977 
978 	status = bibl_fixcharsets( b, &lp );
979 	if ( status!=BIBL_OK ) goto out;
980 
981 	if ( debug_set( p ) ) bibl_verbose( b, "post-fixcharsets", "for bibl_write" );
982 
983 	if ( p->singlerefperfile ) status = bibl_writeeachfp( fp, b, &lp );
984 	else status = bibl_writefp( fp, b, &lp );
985 
986 out:
987 	bibl_freeparams( &lp );
988 	return status;
989 }
990