/* * bibdiff.c * * Copyright (c) Chris Putnam 2017-2020 * * Program and source code released under the GPL version 2 * */ #include #include #include "bibutils.h" #include "intlist.h" #include "args.h" char progname[] = "bibdiff"; /* find_tag_level_matches() * * Find all possible matches for tag/level combinations */ int find_tag_level_matches( fields *f, char *tag, int level, intlist *matchs ) { int i, n = 0; char *ftag; for ( i=0; in; ++i ) intlist_set( matchs, i, 0 ); for ( i=0; in; ++i ) { if ( fields_level( f, i ) != level ) continue; ftag = fields_tag( f, i, FIELDS_CHRP_NOLEN ); if ( !strcmp( tag, ftag ) ) { intlist_set( matchs, i, 1 ); n++; } } return n; } int compare_references( fields *f1, const char *fname1, fields *f2, const char *fname2, long n ) { int i, j, cnt, cnt1, cnt2, level, diff = 0; intlist found1, found2, matches; char *tag, *data; intlist_init_fill( &found1, f1->n, -1 ); intlist_init_fill( &found2, f2->n, -1 ); intlist_init_fill( &matches, f2->n, 0 ); for ( i=0; in; ++i ) { tag = fields_tag( f1, i, FIELDS_CHRP_NOLEN ); data = fields_value( f1, i, FIELDS_CHRP_NOLEN ); level = fields_level( f1, i ); cnt = find_tag_level_matches( f2, tag, level, &matches ); /* ...no matches */ if ( cnt==0 ) continue; for ( j=0; jn; ++j ) { if ( intlist_get( &matches, j ) == 0 ) continue; /* not a potential match */ if ( intlist_get( &found2, j ) != -1 ) continue; /* already claimed */ if ( strcmp( data, fields_value( f2, j, FIELDS_CHRP_NOLEN ) ) ) continue; /* values don't match */ intlist_set( &found1, i, j ); intlist_set( &found2, j, i ); break; } } cnt1 = 0; for ( i=0; in; ++i ) if ( intlist_get( &found1, i ) != -1 ) cnt1++; cnt2 = 0; for ( i=0; in; ++i ) if ( intlist_get( &found2, i ) != -1 ) cnt2++; if ( cnt1!=f1->n || cnt2!=f2->n ) { printf( "reference %ld < %s > %s\n", n, fname1, fname2 ); for ( i=0; in; ++i ) { if ( intlist_get( &found1, i ) != -1 ) continue; tag = fields_tag( f1, i, FIELDS_CHRP_NOLEN ); data = fields_value( f1, i, FIELDS_CHRP_NOLEN ); level = fields_level( f1, i ); printf( "< '%s' '%s' %d\n", tag, data, level ); } for ( i=0; in; ++i ) { if ( intlist_get( &found2, i ) != -1 ) continue; tag = fields_tag( f2, i, FIELDS_CHRP_NOLEN ); data = fields_value( f2, i, FIELDS_CHRP_NOLEN ); level = fields_level( f2, i ); printf( "> '%s' '%s' %d\n", tag, data, level ); } } intlist_free( &found1 ); intlist_free( &found2 ); intlist_free( &matches ); return diff; } int compare_bibliographies( bibl *b1, const char *fname1, bibl *b2, const char *fname2 ) { fields *f1, *f2; long i; if ( b1->n != b2->n ) { printf( "%s: %s has %ld references and %s has %ld references\n", progname, fname1, b1->n, fname2, b2->n ); return 1; } for ( i=0; in; ++i ) { f1 = b1->ref[i]; f2 = b2->ref[i]; if ( compare_references( f1, fname1, f2, fname2, i+1 ) ) return 1; } return 0; } void version( void ) { args_tellversion( progname ); exit( EXIT_FAILURE ); } void help( void ) { args_tellversion( progname ); fprintf( stderr, "Compares references after reading them from their native formats\n\n" ); fprintf( stderr, "usage: %s ref1_file ref2_file\n\n", progname ); fprintf( stderr, "-h, --help display this help\n" ); fprintf( stderr, "-v, --version display version\n" ); fprintf( stderr, "-f1, --format1 FORMAT specify input format for ref1_file\n" ); fprintf( stderr, "-f2, --format2 FORMAT specify input format for ref1_file\n" ); fprintf( stderr, "\n" ); fprintf( stderr, "Valid format specifiers are 'bibtex', 'biblatex', 'copac', 'ebi', " "'endnote', 'endnote-xml', 'medline', 'mods', 'nbib', 'ris', 'word2007'\n\n" ); exit( EXIT_FAILURE ); } int lookup_format( const char *format ) { typedef struct flist_t { char *name; int code; } flist_t; flist_t formats[] = { { "bibtex", BIBL_BIBTEXIN }, { "biblatex", BIBL_BIBLATEXIN }, { "copac", BIBL_COPACIN }, { "ebi", BIBL_EBIIN }, { "endnote", BIBL_ENDNOTEIN }, { "endnote-xml", BIBL_ENDNOTEXMLIN }, { "medline", BIBL_MEDLINEIN }, { "mods", BIBL_MODSIN }, { "nbib", BIBL_NBIBIN }, { "ris", BIBL_RISIN }, { "word2007", BIBL_WORDIN }, }; int i, nformats = sizeof( formats ) / sizeof( formats[0] ); for ( i=0; i