UNB/ CS/ David Bremner/ research/ polybib/ bibfilter.pl
#!/usr/bin/env perl -w

#
# $Id: bibfilter.pl 6369 2007-03-30 12:08:07Z bremner $
#

use strict;
use Text::BibTeX qw(:metatypes :macrosubs);
use Text::BibTeX::Structure;
use Text::BibTeX::Section;
use Text::BibTeX::Formatter;
use Text::BibTeX::Filter;
use Carp qw(croak);
use Getopt::Std;
use Data::Dumper;
use List::Util;

my $debug=1;

my %markopts=(
	      latex=>{
		      journal_mkup=>['\emph{','}'],
		      btitle_mkup=>['\emph{','}'],
		      inter_block_mkup=>"\n".'\newblock ',
		      pre_entry_mkup=>'\nbibitem{%KEY%}{%LABEL%}{%STYLE%}' . "\n",
		      blist_mkup=> '\begin{nbiblist}{%WIDEST%}',
		      elist_mkup=> '\end{nbiblist}',
		      url_mkup=>['\url{','}'],
		      section_1_mkup=>'\section{%TITLE%}\label{sec:%KEY%}',
		      emptysec_1_mkup=>'\refstepcounter{section}',
		      seccounter_1_mkup=>'\thesection',
		      section_2_mkup=>'\subsection{%TITLE%}\label{sec:%KEY%}',
		      seccounter_2_mkup=>'\thesubsection',
		      emptysec_2_mkup=>'\refstepcounter{subsection}',
		      section_3_mkup=>'\subsubsection{%TITLE%}\label{sec:%KEY%}',
		      seccounter_3_mkup=>'\thesubsubsection',
		      emptysec_3_mkup=>'\refstepcounter{subsubsection}',
		      tilde_mkup=>'~',
		      annote_mkup=>['\begin{quote}','\end{quote}%NOPERIOD%']
		     },
	      html=>{
		     journal_mkup=>['<em>','</em>'],
		     btitle_mkup=>['<bf>','</bf>'],
		     inter_block_mkup=>"\n<! new block> ",
		     pre_entry_mkup=>'<dt>%LABEL%</dt><dd>' . "\n",
		     blist_mkup=> '<dl>',
		     elist_mkup=> '</dl>',
		     url_mkup=>['<a href=%URL%>','</a>'],
		     section_1_mkup=>'<h1>%TITLE%</h1>',
		     tilde_mkup=>'&nbsp;',
		     annote_mkup=>'<blockquote>%ANNOTE%</blockquote>'
		    },
	     );

  


sub remap(%$){
  my $mapref=shift;
  my %map=%$mapref;
  my $key=shift;

  my $watchdog=0;
  while (defined($map{$key})){
    die "Circular remap ?" if ($watchdog++ > 1000);
    $key=$map{$key};
    last if (defined($map{$key}) && $map{$key} eq $key);
  }
  return $key;
}

sub closure(@){
  my %map=@_;
  my %closure=();
  foreach my $key (keys %map){
    $closure{$key}=remap(\%map,$key);
  }
  return %closure;
}
sub add_macros(@){
  my %map=closure(@_);
  foreach my $key (keys %map){
#    print STDERR "adding macro $key\n";
    add_macro_text($key,$map{$key});
  }
}

sub process_sections($@){
  my $structure=shift;
  my (@sections)=@_;
  my (@seclist);
  my %macros;
  my $secindex=1;
  while (scalar(@sections)){
    my $sec=shift(@sections);
    my %map=@{shift(@sections)};

    if (defined($map{aliases})){
      foreach my $alias (@{$map{aliases}}){
	$macros{$alias}=$sec;
      }
    }
    $macros{$sec}=$sec;
    # many things ignored
    print STDERR "processing $sec $map{title} $secindex\n" if $debug;
    $structure->add_section(  $sec=>new Text::BibTeX::Section ( KEY=>$sec,
					    TITLE=>$map{title},
					    ANNOTE=>$map{annote},
					    WIDESTLABEL=>$map{widestlabel},
					    STYLE=>$map{style},
					    SORTDIR=>-1,
					    LEVEL=>$map{level},
					    OMITEMPTY=>$map{omitempty},
					    STRUCTURE=>$structure,
					    OPTIONS=>$map{options}) );
  }
  add_macros(%macros);
}


sub openbib($@){
  my $file=shift;
  my @path=@_;
  
  my $bib=undef;


  if ($file=~ m|^/|){
    $bib = new Text::BibTeX::File $file;
  } else {
    foreach my $dir (@path){
      my $path=$dir."/".$file;
      print STDERR "trying $path\n" if $debug;;
      
      $bib = new Text::BibTeX::File $path;
      last if (defined($bib));
    }
  }
  die "$file: $!" if (!defined($bib));
  return $bib;
    
}
my ($conffile,$filename, $structure, $bibfile, $entry, %sortkey, @entries);

my $verbose=1;

die "usage: bibfilter -c config_file -p path  file1 file2\n" unless @ARGV >= 2;

my %args;
getopts("c:p:",\%args);
$conffile=$args{c} || die "config file mandatory";

my $datadirs=$args{p} || ".";

my @bibpath=split(":",$datadirs);

open(CONF,"<$conffile")|| die ("could not open $conffile");
my @conf=<CONF>;
close(CONF);

my @options;
eval("\@options=(".join('',@conf).")");
die "reading configuration failed: $@" if $@;
my %option=@options;

my $structname=$option{structure} || 'CV';

my $markup=$option{markup} || 'latex';

my %bibopts=$option{biboptions} ? @{$option{biboptions}} : ();
  

print STDERR "creating a $structname structure\n" if $debug;


my $bibstructure=new Text::BibTeX::Structure($structname,%bibopts,
					     %{$markopts{$markup}});

# make a first pass parsing files full of macros (@strings)

if ($option{macro_files}){
  foreach my $macname (@{$option{macro_files}}){
    $bibfile = openbib($macname,@bibpath);

    while ($entry = new Text::BibTeX::Entry $bibfile)
    {
      next unless $entry->parse_ok && $entry->metatype == BTE_MACRODEF;
      $entry->check if ($option{check});
    }
    $bibfile->close;
  }
}

my %macro=();
%macro=@{$option{macros}} if ($option{macros});
add_macros(%macro);





my @seclist=();
@seclist=@{$option{sections}} if ($option{sections});

my $bibname=$option{bibname} || "bfout";


process_sections($bibstructure,@seclist);

my @datafiles=@ARGV;
@datafiles=(@datafiles, @{$option{data_files}}) if (exists $option{data_files});
foreach my $filename (@datafiles){
  my $bib;
  my @path=@bibpath;
  @path=('') if  ($filename=~ m|^/|);
 TRY: foreach my $dir (@path){
    my $path=$dir."/".$filename;
    print STDERR "trying $path\n" if $debug;
    
    $bib = new Text::BibTeX::Filter $path,$bibstructure,\%macro,%option;
    last TRY if (defined($bib));
  }

 ENTRY: while (my $entry=$bib->read()){
  }

}

open TEX, ">$bibname.bbl" || die "$!";

$\="\n";


my @outlist=sort { $a->index <=> $b->index } values %{$bibstructure->sections}; 
foreach my $sec (@outlist){
  my $entry;
  my $count=0;

  $sec->sort;

  my $fmt = new Text::BibTeX::Formatter(SECTION=>$sec);

  if ($sec->count==0 && $sec->omitempty){
    print TEX $fmt->emptysection
  } else {
    print TEX $fmt->secheader;
    print TEX $fmt->start_list;
    my @entries=@{$sec->entries};

    while ($entry = shift @entries){
      print TEX $fmt->format($entry);
    }
    print TEX $fmt->end_list;
  }


}