2 # bibtex_to_paper opens the paper corresponding to a bibtex key
3 # and is released under the terms of the GNU GPL version 3, or any
4 # later version, at your option. See the file README and COPYING for
6 # Copyright 2014 by Don Armstrong <don@donarmstrong.com>.
16 use File::Basename qw(basename);
17 use File::Spec qw(rel2abs);
26 bibtex_to_paper - opens the paper corresponding to a bibtex key
30 bibtex_to_paper [options] bibtexkey
33 --bibtex, -b bibtex file to look up key in
34 --bibtex-cache, -c bibtex cache file
35 --build-cache, -B build cache using bibtex files
36 --pdf-dir pdf directory
37 --pdfviewer, -p pdf viewer to use
38 --debug, -d debugging level (Default 0)
39 --help, -h display this help
40 --man, -m display manual
48 Bibtex file to look key up in
50 =item B<--bibtex-cache, -c>
52 Bibtex cache file; rebuilt if bibtex file changes
54 =item B<--pdfviewer, -p>
56 PDF viewer to use; defaults to evince unless a .xoj exists, in which
61 Debug verbosity. (Default 0)
65 Display brief usage information.
82 my %options = (debug => 0,
85 'bibtex_cache' => File::Spec->catfile(User->Home,'.bibtex_to_paper_cache'),
89 'build_cache|build-cache!',
91 'bibtex_cache|bibtex-cache|c=s',
93 'papers_directory|papers-directory=s@',
94 'debug|d+','help|h|?','man|m');
96 pod2usage() if $options{help};
97 pod2usage({verbose=>2}) if $options{man};
99 $DEBUG = $options{debug};
102 if (not exists $options{bibtex} and
103 not exists $options{bibtex_cache}) {
105 "You must give at least one of --bibtex".
109 pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
117 if (exists $options{bibtex_cache}) {
119 if (-e $options{bibtex_cache}) {
120 ($dbh,$sth) = open_cache($options{bibtex_cache});
122 ($dbh,$sth) = initialize_database($options{bibtex_cache});
127 if (exists $options{build_cache}) {
128 $options{bibtex} //= [];
131 @{ref $options{bibtex}?$options{bibtex}:[$options{bibtex}]},
135 if (exists $options{bibtex}) {
136 for my $bibtex_file (@{ref $options{bibtex}?$options{bibtex}:[$options{bibtex}]}) {
137 parse_bibtex_file($bibtex_file,\%entries);
141 if (exists $options{papers_directory} and
144 load_papers_into_database($dbh,$sth,$options{papers_directory});
147 p %entries if $DEBUG;
148 if (keys %entries and
150 load_bibtex_entries_into_database($dbh,$sth,\%entries);
154 for my $bibtex_key (@ARGV) {
155 open_bibtex_key(\%options,$dbh,$sth,\%entries,$bibtex_key);
160 sub load_papers_into_database {
161 my ($dbh,$sth,$dir) = @_;
163 my @dirs = ref($dir)?@{$dir}:$dir;
165 my $actually_load_it = sub {
166 return unless /\.pdf$/;
171 insert_or_replace_papers($dbh,$sth,basename($File::Find::name),File::Spec->rel2abs($_),$xoj);
175 find($actually_load_it,@dirs);
178 sub insert_or_replace_papers {
179 my ($dbh,$sth,$file_name,$file_loc,$has_xoj) = @_;
180 $sth->{insert_papers}->execute($file_name,$file_loc,$has_xoj);
181 $sth->{insert_papers}->finish();
184 sub load_bibtex_entries_into_database {
185 my ($dbh,$sth,$entries) = @_;
186 for my $entry (keys %{$entries}) {
187 next unless defined $entries->{$entry};
188 $sth->{insert_bibtex}->execute($entry,@{$entries->{$entry}}{qw(file_name doi html)});
189 $sth->{insert_bibtex}->finish();
190 print STDERR "inserted $entry {".join(',',map {defined $_?"'$_'":"'undef'"} %{$entries->{$entry}})."}\n" if $DEBUG;
194 sub open_bibtex_key {
195 my ($options,$dbh,$sth,$entries,$bibtex_key) = @_;
196 if (not defined $dbh) {
197 open_entry($dbh,$sth,$entries->{$bibtex_key},$options);
199 my $entry = select_entry_from_bibtex_key($dbh,$sth,$bibtex_key);
201 open_entry($dbh,$sth,$entry,$options);
208 if (not defined $child) {
209 die "Unable to fork for some reason: $!";
220 my ($file_name,$options,$has_xoj) = @_;
221 print STDERR "opening $file_name\n" if $DEBUG;
223 fork_exec('xournal',$file_name);
225 fork_exec('evince',$file_name)
231 fork_exec('sensible-browser',$file);
235 my ($dbh,$sth,$entry,$options) = @_;
237 return unless defined $entry and ref $entry and keys %{$entry};
238 if (defined $entry->{file_name} and length $entry->{file_name}) {
239 my $paper = select_one($dbh,$sth->{select_papers_by_name},$entry->{file_name});
241 print STDERR $entry->{file_name} if $DEBUG;
242 if (defined $paper) {
243 open_pdf($paper->{path},$options,$paper->{has_xoj});
247 if (defined $entry->{doi}) {
248 my $url = $entry->{doi};
249 $url =~ s{^doi://}{http://dx.doi.org/};
250 open_browser($url,$options);
253 if (defined $entry->{html}) {
254 open_browser($entry->{html},$options);
259 sub select_entry_from_bibtex_key{
260 my ($dbh,$sth,$bibtex_key) = @_;
262 my $entry = select_one($dbh,$sth->{select_bibtex_by_key},$bibtex_key);
263 if (not defined $entry) {
264 $bibtex_key =~ s/:.*$//;
265 $entry = select_one($dbh,$sth->{select_bibtex_by_approximate_key},$bibtex_key.'%');
271 my ($dbh,$sth,@bind_vals) = @_;
272 $sth->execute(@bind_vals) or
273 die "Unable to select one: ".$dbh->errstr();
274 my $results = $sth->fetchall_arrayref({});
276 return ref($results)?$results->[0]:undef;
279 sub parse_bibtex_file {
280 my ($file,$entries) = @_;
282 my $bibfile = Text::BibTeX::File->new($file)
283 or die "Unable to open $file for reading: $!";
286 while ($entry = Text::BibTeX::Entry->new($bibfile)) {
287 print STDERR "In Entry ".$entry->metatype() if $DEBUG;
288 if ($entry->metatype() == BTE_COMMENT) {
289 push @entry_comments,$entry->value();
290 } elsif ($entry->metatype() == BTE_REGULAR) {
291 my $entry_key = $entry->key();
292 if (not defined $entry_key) {
293 @entry_comments = ();
297 # if there is a file comment, use it as the file name
298 for my $comment (@entry_comments) {
299 next unless $comment =~ /^\s*file(?:name)?:?\s*(.+?)\s*$/i;
300 next unless length $1;
301 $entry_data{file_name} = $1.'.pdf';
304 my %field_prefix = (doi => 'doi://',
308 my %field_name = (doi => 'doi',
310 file => 'file_name',);
311 for my $field (qw(file doi html)) {
312 my $field_value = $entry->get($field);
313 if (defined $field_value and $field_value =~ /\S+/) {
314 $entry_data{$field_name{$field}} =
315 $field_prefix{$field}.$field_value if
316 not defined $entry_data{$field_name{$field}};
319 $entries->{$entry_key} = {} if not defined $entries->{$entry_key};
320 for my $field (keys %entry_data) {
321 $entries->{$entry_key}{$field} = $entry_data{$field} if
322 defined $entry_data{$field};
324 # reset the entry comments
325 @entry_comments = ();
329 print STDERR "\n" if $DEBUG;
335 sub initialize_database {
337 return open_cache($cache,1);
341 my ($cache,$initialize) = @_;
342 my $dbh = DBI->connect("dbi:SQLite:dbname=$cache","","") or
343 die "Unable to open/create database $cache";
345 $dbh->do("DROP TABLE IF EXISTS bibtex;");
346 $dbh->do("DROP TABLE IF EXISTS papers;");
348 CREATE TABLE bibtex (
349 bibtex_key TEXT PRIMARY KEY,
356 CREATE UNIQUE INDEX bibtex_file_name ON bibtex(file_name);
359 CREATE UNIQUE INDEX bibtex_bibtex_key ON bibtex(bibtex_key);
362 CREATE TABLE papers (
363 file_name TEXT PRIMARY KEY,
369 CREATE UNIQUE INDEX papers_path ON papers(path);
372 CREATE UNIQUE INDEX papers_file_name ON papers(file_name);
376 (insert_papers => <<'EOF',
377 INSERT OR REPLACE INTO papers(file_name,path,has_xoj) VALUES (?,?,?);
379 insert_bibtex => <<'EOF',
380 INSERT OR REPLACE INTO bibtex (bibtex_key,file_name,doi,html) VALUES (?,?,?,?);
382 select_papers_by_name => <<'EOF',
383 SELECT * FROM papers WHERE file_name = ?;
385 select_papers_by_path => <<'EOF',
386 SELECT * FROM papers WHERE path = ?;
388 select_bibtex_by_key => <<'EOF',
389 SELECT * FROM bibtex WHERE bibtex_key = ?;
391 select_bibtex_by_approximate_key => <<'EOF',
392 SELECT * FROM bibtex WHERE bibtex_key LIKE ?;
394 select_bibtex_by_file_name => <<'EOF',
395 SELECT * FROM bibtex WHERE file_name = ?;
399 for my $key (keys %s) {
400 $st->{$key}=$dbh->prepare($s{$key}) //
401 die "Unable to prepare sql statement: ".$dbh->errstr;