#!/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=>' ',
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;
}
}