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 'clear_cache|clear-cache!',
94 'papers_directory|papers-directory=s@',
95 'debug|d+','help|h|?','man|m');
97 pod2usage() if $options{help};
98 pod2usage({verbose=>2}) if $options{man};
100 $DEBUG = $options{debug};
103 if (not exists $options{bibtex} and
104 not exists $options{bibtex_cache}) {
106 "You must give at least one of --bibtex".
110 pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
118 if (exists $options{bibtex_cache}) {
120 if (-e $options{bibtex_cache}) {
121 ($dbh,$sth) = open_cache($options{bibtex_cache});
123 ($dbh,$sth) = initialize_database($options{bibtex_cache});
127 if (exists $options{clear_cache}) {
128 clear_cache($dbh,$sth);
131 if (exists $options{build_cache}) {
132 $options{bibtex} //= [];
135 @{ref $options{bibtex}?$options{bibtex}:[$options{bibtex}]},
139 if (exists $options{bibtex}) {
140 for my $bibtex_file (@{ref $options{bibtex}?$options{bibtex}:[$options{bibtex}]}) {
141 parse_bibtex_file($bibtex_file,\%entries);
145 if (exists $options{papers_directory} and
148 load_papers_into_database($dbh,$sth,$options{papers_directory});
151 p %entries if $DEBUG;
152 if (keys %entries and
154 load_bibtex_entries_into_database($dbh,$sth,\%entries);
158 for my $bibtex_key (@ARGV) {
159 open_bibtex_key(\%options,$dbh,$sth,\%entries,$bibtex_key);
166 $sth->{clear_papers_cache}->execute();
167 $sth->{clear_bibtex_cache}->execute();
170 sub load_papers_into_database {
171 my ($dbh,$sth,$dir) = @_;
173 my @dirs = ref($dir)?@{$dir}:$dir;
175 my $actually_load_it = sub {
176 return unless /\.pdf$/;
181 insert_or_replace_papers($dbh,$sth,basename($File::Find::name),File::Spec->rel2abs($_),$xoj);
185 find($actually_load_it,@dirs);
188 sub insert_or_replace_papers {
189 my ($dbh,$sth,$file_name,$file_loc,$has_xoj) = @_;
190 $sth->{insert_papers}->execute($file_name,$file_loc,$has_xoj);
191 $sth->{insert_papers}->finish();
194 sub load_bibtex_entries_into_database {
195 my ($dbh,$sth,$entries) = @_;
196 for my $entry (keys %{$entries}) {
197 next unless defined $entries->{$entry};
198 $sth->{insert_bibtex}->execute($entry,@{$entries->{$entry}}{qw(file_name doi html)});
199 $sth->{insert_bibtex}->finish();
200 print STDERR "inserted $entry {".join(',',map {defined $_?"'$_'":"'undef'"} %{$entries->{$entry}})."}\n" if $DEBUG;
204 sub open_bibtex_key {
205 my ($options,$dbh,$sth,$entries,$bibtex_key) = @_;
206 if (not defined $dbh) {
207 open_entry($dbh,$sth,$entries->{$bibtex_key},$options);
209 my $entry = select_entry_from_bibtex_key($dbh,$sth,$bibtex_key);
211 open_entry($dbh,$sth,$entry,$options);
218 if (not defined $child) {
219 die "Unable to fork for some reason: $!";
230 my ($file_name,$options,$has_xoj) = @_;
231 print STDERR "opening $file_name\n" if $DEBUG;
233 fork_exec('xournal',$file_name);
235 fork_exec('evince',$file_name)
241 fork_exec('sensible-browser',$file);
245 my ($dbh,$sth,$entry,$options) = @_;
247 return unless defined $entry and ref $entry and keys %{$entry};
248 if (defined $entry->{file_name} and length $entry->{file_name}) {
249 my $paper = select_one($dbh,$sth->{select_papers_by_name},$entry->{file_name});
251 print STDERR $entry->{file_name} if $DEBUG;
252 if (defined $paper) {
253 open_pdf($paper->{path},$options,$paper->{has_xoj});
257 if (defined $entry->{doi}) {
258 my $url = $entry->{doi};
259 $url =~ s{^doi://}{http://dx.doi.org/};
260 open_browser($url,$options);
263 if (defined $entry->{html}) {
264 open_browser($entry->{html},$options);
269 sub select_entry_from_bibtex_key{
270 my ($dbh,$sth,$bibtex_key) = @_;
272 my $entry = select_one($dbh,$sth->{select_bibtex_by_key},$bibtex_key);
273 if (not defined $entry) {
274 $bibtex_key =~ s/:.*$//;
275 $entry = select_one($dbh,$sth->{select_bibtex_by_approximate_key},$bibtex_key.'%');
281 my ($dbh,$sth,@bind_vals) = @_;
282 $sth->execute(@bind_vals) or
283 die "Unable to select one: ".$dbh->errstr();
284 my $results = $sth->fetchall_arrayref({});
286 return ref($results)?$results->[0]:undef;
289 sub parse_bibtex_file {
290 my ($file,$entries) = @_;
292 my $bibfile = Text::BibTeX::File->new($file)
293 or die "Unable to open $file for reading: $!";
296 while ($entry = Text::BibTeX::Entry->new($bibfile)) {
297 print STDERR "In Entry ".$entry->metatype() if $DEBUG;
298 if ($entry->metatype() == BTE_COMMENT) {
299 push @entry_comments,$entry->value();
300 } elsif ($entry->metatype() == BTE_REGULAR) {
301 my $entry_key = $entry->key();
302 if (not defined $entry_key) {
303 @entry_comments = ();
307 # if there is a file comment, use it as the file name
308 for my $comment (@entry_comments) {
309 next unless $comment =~ /^\s*file(?:name)?:?\s*(.+?)\s*$/i;
310 next unless length $1;
311 $entry_data{file_name} = $1.'.pdf';
314 my %field_prefix = (doi => 'doi://',
318 my %field_name = (doi => 'doi',
320 file => 'file_name',);
321 for my $field (qw(file doi html)) {
322 my $field_value = $entry->get($field);
323 if (defined $field_value and $field_value =~ /\S+/) {
324 $entry_data{$field_name{$field}} =
325 $field_prefix{$field}.$field_value if
326 not defined $entry_data{$field_name{$field}};
329 $entries->{$entry_key} = {} if not defined $entries->{$entry_key};
330 for my $field (keys %entry_data) {
331 $entries->{$entry_key}{$field} = $entry_data{$field} if
332 defined $entry_data{$field};
334 # reset the entry comments
335 @entry_comments = ();
339 print STDERR "\n" if $DEBUG;
345 sub initialize_database {
347 return open_cache($cache,1);
351 my ($cache,$initialize) = @_;
352 my $dbh = DBI->connect("dbi:SQLite:dbname=$cache","","") or
353 die "Unable to open/create database $cache";
355 $dbh->do("DROP TABLE IF EXISTS bibtex;");
356 $dbh->do("DROP TABLE IF EXISTS papers;");
358 CREATE TABLE bibtex (
359 bibtex_key TEXT PRIMARY KEY,
366 CREATE UNIQUE INDEX bibtex_file_name ON bibtex(file_name);
369 CREATE UNIQUE INDEX bibtex_bibtex_key ON bibtex(bibtex_key);
372 CREATE TABLE papers (
373 file_name TEXT PRIMARY KEY,
379 CREATE UNIQUE INDEX papers_path ON papers(path);
382 CREATE UNIQUE INDEX papers_file_name ON papers(file_name);
386 (insert_papers => <<'EOF',
387 INSERT OR REPLACE INTO papers(file_name,path,has_xoj) VALUES (?,?,?);
389 insert_bibtex => <<'EOF',
390 INSERT OR REPLACE INTO bibtex (bibtex_key,file_name,doi,html) VALUES (?,?,?,?);
392 select_papers_by_name => <<'EOF',
393 SELECT * FROM papers WHERE file_name = ?;
395 select_papers_by_path => <<'EOF',
396 SELECT * FROM papers WHERE path = ?;
398 select_bibtex_by_key => <<'EOF',
399 SELECT * FROM bibtex WHERE bibtex_key = ?;
401 select_bibtex_by_approximate_key => <<'EOF',
402 SELECT * FROM bibtex WHERE bibtex_key LIKE ?;
404 select_bibtex_by_file_name => <<'EOF',
405 SELECT * FROM bibtex WHERE file_name = ?;
407 clear_papers_cache => <<'EOF',
410 clear_bibtex_cache => <<'EOF',
415 for my $key (keys %s) {
416 $st->{$key}=$dbh->prepare($s{$key}) //
417 die "Unable to prepare sql statement: ".$dbh->errstr;