├── papers2bib
├── bibhtmltable
├── bibmerge
├── bibdump
├── README
├── papers_library.rb
└── bib_library.rb
/papers2bib:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby1.9
2 |
3 | $:.unshift(File.dirname(__FILE__))
4 |
5 | require 'bib_library.rb'
6 | require 'papers_library.rb'
7 | require 'getoptlong'
8 | require 'kconv'
9 |
10 | $stdout.set_encoding("EUC-JP", "UTF-8")
11 |
12 | #
13 | # main
14 | #
15 |
16 | if __FILE__ == $0
17 | require 'optparse'
18 |
19 | opts = { # default options
20 | :mode => "plain",
21 | :draft => false,
22 | :encoding => "UTF-8",
23 | :key_file => "" # if it's null string, try to generate file by basename
24 |
25 | }
26 |
27 | ARGV.options do |o|
28 | o.banner = "ruby #{$0} [options] Papers_BIB_File"
29 | o.separator "Options:"
30 |
31 | o.on("-K KEY_FILE", "--key-file KEY_FILE", "Specify keywords file") {|x| opts[:key_file] = x }
32 |
33 | o.parse!
34 |
35 | end
36 |
37 | if ARGV.size == 1
38 | bl = PapersBibLibrary.new(ARGV[0], opts)
39 | bl.out($stdout)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/bibhtmltable:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #
3 | # Read bib file and create link output for
4 | #
5 | #
6 |
7 | $:.unshift(File.dirname(__FILE__))
8 |
9 | require 'bib_library.rb'
10 | require 'papers_library.rb'
11 | require 'getoptlong'
12 | require 'kconv'
13 | require 'date'
14 | require 'ap'
15 |
16 | $stdout.set_encoding("UTF-8")
17 |
18 | #
19 | # main
20 | #
21 |
22 | if __FILE__ == $0
23 | require 'optparse'
24 |
25 | opts = { # default options
26 | :mode => "plain",
27 | :draft => false,
28 | :encoding => "UTF-8",
29 | :output_encoding => "UTF-8",
30 | :key_file => "", # if it's null string, try to generate file by basename
31 | :bib_dir => "./bib",
32 | :force => false,
33 | :timestamp => true,
34 | :write => false,
35 | :list => false,
36 | :mismatch_dump => false
37 | }
38 |
39 | ARGV.options do |o|
40 | o.banner = "ruby #{$0} [options] BIB_File {cite_keys..}"
41 | o.separator "Options:"
42 | o.parse!
43 | end
44 |
45 | if ARGV.size >= 1
46 | blib = BibLibrary.new(ARGV.shift, opts)
47 | cite_keys = blib.keys.sort
48 | if ARGV.size > 0
49 | cite_keys = ARGV
50 | end
51 |
52 | cite_keys.each do |ck|
53 | bib = blib[ck]
54 | bib.prep
55 | au = bib.authors || [ ]
56 | f = "#{ck}.pdf"
57 | f.gsub!(/:/, '_')
58 | puts ""
59 | puts "| #{ck} | "
60 | puts "#{bib.title} | "
61 | puts "#{au.join(', ')} | "
62 | puts "
"
63 | end
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/bibmerge:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #
3 | # Read several bib files and merge into one
4 | #
5 | # check the values of:
6 | # date-written-via-bibdump
7 | # date-modified
8 | #
9 |
10 | $:.unshift(File.dirname(__FILE__))
11 |
12 | require 'bib_library.rb'
13 | require 'papers_library.rb'
14 | require 'getoptlong'
15 | require 'kconv'
16 | require 'date'
17 |
18 | $stdout.set_encoding("EUC-JP", "UTF-8")
19 |
20 | #
21 | # main
22 | #
23 |
24 | if __FILE__ == $0
25 | require 'optparse'
26 |
27 | opts = { # default options
28 | :mode => "plain",
29 | :draft => false,
30 | :encoding => "UTF-8",
31 | :output_encoding => "UTF-8",
32 | :key_file => "", # if it's null string, try to generate file by basename
33 | :bib_dir => "./bib",
34 | :force => false,
35 | :timestamp => true,
36 | :add => false,
37 | :overwrite => false,
38 | :group => nil,
39 | :dep_dump => false
40 | }
41 |
42 | ARGV.options do |o|
43 | o.banner = "ruby #{$0} [options] BIB_File bib_keys..."
44 | o.separator "Options:"
45 | o.on("--utf-8", "-u", "Set both i/o encoding to UTF-8") {|x| opts[:encoding] = "UTF-8" ; opts[:output_encoding] = opts[:encoding] }
46 | o.on("--euc-jp", "-e", "Set both i/o encoding to EUC-JP") {|x| opts[:encoding] = "EUC-JP" ; opts[:output_encoding] = opts[:encoding] }
47 | o.on("--sjis", "-s", "Set both i/o encoding to Shift_JIS") {|x| opts[:encoding] = "Shift_JIS" ; opts[:output_encoding] = opts[:encoding] }
48 |
49 | o.on("--output-utf-8", "Set output encoding to UTF-8") {|x| opts[:output_encoding] = "UTF-8" }
50 | o.on("--output-euc-jp", "Set output encoding to EUC-JP") {|x| opts[:output_encoding] = "EUC-JP" }
51 | o.on("--output-sjis", "Set output encoding to Shift_JIS") {|x| opts[:output_encoding] = "Shift_JIS" }
52 |
53 | o.on("--bib-dir", "-B DIR", "Set bib dir (currently ./bib)") {|x| opts[:bib_dir] = x }
54 |
55 | o.on("--force", "-f", "Force to overwrite files") {|x| opts[:force] = true }
56 | o.on("--no-time-stamp", "-t", "don't add bibdump date") {|x| opts[:timestamp] = false }
57 | o.on("--add", "-a", "Add to primary if missing") {|x| opts[:add] = true }
58 | o.on("--overwrite", "-o", "Do overwrite if needed") {|x| opts[:overwrite] = true }
59 | o.on("--dump-mismatch", "-d", "Dump mismatched entries") {|x| opts[:dump_mismatch] = true }
60 |
61 | o.on("--group GROUP", "-G GROUP", "Configure group mode") {|x| opts[:group] = x }
62 |
63 | o.on("--dep-dump", "-p", "Dump dependencies") {|x| opts[:dep_dump] = true }
64 |
65 | o.parse!
66 | end
67 |
68 |
69 | if ARGV.size >= 1
70 | if opts[:dep_dump] == true
71 | blib = BibLibrary.new(nil, opts)
72 | ARGV.each do |f|
73 | bibfile = blib.mkbibpath(f)
74 | if bibfile == nil
75 | STDERR.puts "#{f} not found"
76 | else
77 | puts bibfile
78 | end
79 | end
80 | else
81 | blib = BibLibrary.new(ARGV.shift, opts)
82 | ARGV.each do |f|
83 | bibfile = blib.mkbibpath(f)
84 | if bibfile == nil
85 | STDERR.puts "#{f} not found"
86 | else
87 | new_blib = BibLibrary.new(bibfile, opts)
88 | blib.merge(new_blib)
89 | end
90 | end
91 | blib.out(STDOUT)
92 | end
93 | end
94 | end
95 |
--------------------------------------------------------------------------------
/bibdump:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #
3 | # Read bib file and check the difference for merge
4 | #
5 |
6 | $:.unshift(File.dirname(__FILE__))
7 |
8 | require 'bib_library.rb'
9 | require 'papers_library.rb'
10 | require 'getoptlong'
11 | require 'kconv'
12 | require 'date'
13 | require 'ap'
14 |
15 | $stdout.set_encoding("EUC-JP", "UTF-8")
16 |
17 | #
18 | # main
19 | #
20 |
21 | if __FILE__ == $0
22 | require 'optparse'
23 |
24 | opts = { # default options
25 | :mode => "plain",
26 | :draft => false,
27 | :encoding => "UTF-8",
28 | :output_encoding => "UTF-8",
29 | :key_file => "", # if it's null string, try to generate file by basename
30 | :bib_dir => "./bib",
31 | :force => false,
32 | :timestamp => true,
33 | :write => false,
34 | :list => false,
35 | :mismatch_dump => false
36 | }
37 |
38 | ARGV.options do |o|
39 | o.banner = "ruby #{$0} [options] BIB_File {cite_keys..}"
40 | o.separator "Options:"
41 | o.on("--utf-8", "-u", "Set both i/o encoding to UTF-8") {|x| opts[:encoding] = "UTF-8" ; opts[:output_encoding] = opts[:encoding] }
42 | o.on("--euc-jp", "-e", "Set both i/o encoding to EUC-JP") {|x| opts[:encoding] = "EUC-JP" ; opts[:output_encoding] = opts[:encoding] }
43 | o.on("--sjis", "-s", "Set both i/o encoding to Shift_JIS") {|x| opts[:encoding] = "Shift_JIS" ; opts[:output_encoding] = opts[:encoding] }
44 |
45 | o.on("--output-utf-8", "Set output encoding to UTF-8") {|x| opts[:output_encoding] = "UTF-8" }
46 | o.on("--output-euc-jp", "Set output encoding to EUC-JP") {|x| opts[:output_encoding] = "EUC-JP" }
47 | o.on("--output-sjis", "Set output encoding to Shift_JIS") {|x| opts[:output_encoding] = "Shift_JIS" }
48 |
49 | o.on("--bib-dir", "-B DIR", "Set bib dir (currently ./bib)") {|x| opts[:bib_dir] = x }
50 |
51 | o.on("--force", "-f", "Force to overwrite files") {|x| opts[:force] = true }
52 | o.on("--no-time-stamp", "-t", "don't add bibdump date") {|x| opts[:timestamp] = false }
53 | o.on("--write", "-w", "Write out each entries in each file in the output-dir") {|x| opts[:write] = true }
54 | o.on("--list", "-l", "List bib cite keys") {|x| opts[:list] = true }
55 |
56 | o.parse!
57 | end
58 |
59 | if ARGV.size >= 1
60 | blib = BibLibrary.new(ARGV.shift, opts)
61 | cite_keys = blib.keys.sort
62 | if ARGV.size > 0
63 | cite_keys = ARGV
64 | end
65 |
66 | # List cite keys. If parameters are given, only list that key if exists
67 | if opts[:list]
68 | cite_keys.each do |k|
69 | puts k if blib.has_key?(k)
70 | end
71 |
72 | # Write out each entries in the dir,
73 | elsif opts[:write]
74 | marker_lines = []
75 | if opts[:timestamp]
76 | marker_lines = [ ", date-written-via-bibdump = {#{DateTime.now.to_s}}" ]
77 | end
78 | unless File.directory?(opts[:bib_dir])
79 | puts "Directory #{opts[:bib_dir]} does not exists"
80 | else
81 | cite_keys.each do |c|
82 | unless blib.has_key?(c)
83 | puts "cite key <#{c}> does not exist"
84 | else
85 | e = blib[c]
86 | file = blib.mkbibpath(c, true)
87 | if File.file?(file) and opts[:force] == false
88 | puts "File #{file} alrady exists -- don't overwrite"
89 | else
90 | e.write_to_file(file, opts, marker_lines)
91 | end
92 | end
93 | end
94 | end
95 | end
96 | end
97 | end
98 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Uitlity scripts for Papers
2 |
3 | Requires Ruby 1.9 for internationalized code handling.
4 |
5 | Papers is available from
6 | http://mekentosj.com/papers/
7 |
8 |
9 | * papers2bib
10 |
11 | Modify bib library exported from Papers which can be handled by
12 | bibtex.
13 |
14 | To use: Export from Papers, then feed into this script, like:
15 |
16 | papers2bib exported.bib > converted.bib
17 |
18 | Note: the output code is EUC at this moment. Import code is assumed
19 | to be UTF-8.
20 |
21 |
22 | If keywords file given, the names listed in the file (one per each
23 | line) will be treated as keywords, and quote with brances in any of
24 | journal, title or authors field.
25 |
26 | Options:
27 |
28 | -K KEY_FILE or --key-file KEY_FILE
29 | Specifying keywords file. If not specified, keywords file
30 | will be consulted with the name "BIB_FILE_BASENAME-keywords.txt"
31 |
32 |
33 | * bibdump
34 |
35 | Read bib file and dump.
36 |
37 | Options:
38 | -u, --utf-8 Set both i/o encoding to UTF-8
39 | -e, --euc-jp Set both i/o encoding to EUC-JP
40 | -s, --sjis Set both i/o encoding to Shift_JIS
41 | --output-utf-8 Set output encoding to UTF-8
42 | --output-euc-jp Set output encoding to EUC-JP
43 | --output-sjis Set output encoding to Shift_JIS
44 | -B, --bib-dir DIR Set output dir (currently ./bib)
45 | -f, --force Force to overwrite files
46 | -t, --no-time-stamp don't add bibdump date
47 | -w, --write Write out each entries in each file in the output-dir
48 | -l, --list List bib cite keys
49 |
50 | Usage:
51 |
52 | To write out per-bib entry file from BIBFILE.bib (in EUC-JP):
53 | bibdump -w -e BIBFILE.bib
54 |
55 | To write out per-bib entry file from BIBFILE.bib (in EUC-JP), output in UTF-8
56 | bibdump -w -e --output-utf-8 BIBFILE.bib
57 |
58 | To list the citation key in the file (you must give correct encoding):
59 | bibdump -l BIBFILE.bib
60 |
61 | Note: output filename is same as the citation key with suffix ".bib"
62 |
63 |
64 | * bibmerge
65 |
66 | Merge given bibs onto primary bib file BIB_FILE
67 |
68 | ruby ./bibmerge [options] BIB_File {cite_keys|bib_files}..
69 | Options:
70 | -u, --utf-8 Set both i/o encoding to UTF-8
71 | -e, --euc-jp Set both i/o encoding to EUC-JP
72 | -s, --sjis Set both i/o encoding to Shift_JIS
73 | --output-utf-8 Set output encoding to UTF-8
74 | --output-euc-jp Set output encoding to EUC-JP
75 | --output-sjis Set output encoding to Shift_JIS
76 | -B, --bib-dir DIR Set bib dir (currently ./bib)
77 | -f, --force Force to overwrite files
78 | -t, --no-time-stamp don't add bibdump date
79 | -a, --add Add to primary if missing
80 | -o, --overwrite Do overwrite if needed
81 | -d, --dump-mismatch Dump mismatched entries
82 | -p, --dep-dump Dump the list of BIB files bibmerge actually found and merged
83 | -G GROUP, --group GROUP add GROUP as part of suffix (using .{GROUP}-bib prior to .bib)
84 |
85 |
86 | * Japanese Name Hacks
87 |
88 | If there is "JapaneseAutorMap:.*" string in a note of the entry in the
89 | Papers applications' paper entries, strings following it is treated as
90 | mapping between English name into Japanese name. For example,
91 |
92 | JapanesAuthorMap: Yamada Taro: _YAMADA_ _TARO_, Yamada Jiro: _YAMADA_ _JIRO_
93 |
94 | # _NAME_ denotes kanji characters
95 |
96 | Maps english napes into Japanese name, respectively.
97 |
--------------------------------------------------------------------------------
/papers_library.rb:
--------------------------------------------------------------------------------
1 | # Bibliography entry handler for Papers
2 |
3 | require 'bib_library'
4 |
5 | class PapersBibEntry < BibEntry
6 | def initialize(lib, tag, citekey, lines)
7 | super(lib, tag, citekey, lines)
8 | @author_map = { }
9 | end
10 |
11 | def add_author_map(e, j)
12 | @author_map[e] = j
13 | end
14 |
15 | def apply_author_map(r)
16 | if @author_map.size != 0
17 | old_r = r
18 | @author_map.each do |e, j|
19 | r.sub!(/#{e}/, j)
20 | end
21 | end
22 | r
23 | end
24 |
25 | def prep
26 | super
27 | @article_type = :none
28 | @lines.each do |line|
29 | if line =~ /\s*([\w-]+)\s*=\s*(.*)$/
30 | l, r = $1, $2
31 | if l=~/keywords/
32 | if r =~/specification/
33 | @article_type = :specification
34 | elsif l=~/keywords/ and r =~/site/
35 | @article_type = :site
36 | end
37 | end
38 | end
39 | end
40 | papers2bibtex
41 | end
42 |
43 | def out(o)
44 | o.puts "% " + "-"*(76 - @citekey.size) + " #{@citekey}"
45 | o.puts ""
46 | l, r = "", ""
47 |
48 | # preparation passes..
49 | @lines.each do |line|
50 | if line =~ /\s*([\w-]+)\s*=\s*(.*)$/
51 | l, r = $1, $2
52 | pn = l.gsub(/\-/,"_").downcase
53 | begin
54 | l, r = eval("do_rec_#{pn}_prep(l, r)")
55 | rescue NoMethodError
56 | # can't find processing lib
57 | end
58 | end
59 | end
60 |
61 | # output passes.
62 | @lines.each do |line|
63 | if line =~ /\s*([\w-]+)\s*=\s*(.*)$/
64 | l, r = $1, $2
65 | pn = l.gsub(/\-/,"_").downcase
66 | begin
67 | l, r = eval("do_rec_#{pn}(l, r)")
68 | rescue NoMethodError
69 | # can't find processing lib
70 | end
71 | begin
72 | o.puts " #{l} = #{r}"
73 | rescue Encoding::UndefinedConversionError
74 | STDERR.puts "!!! Can't Convert: #{l} = #{r}"
75 | end
76 | else
77 | o.puts line
78 | end
79 | end
80 | o.puts ""
81 | end
82 |
83 | def papers2bibtex
84 | begin
85 | eval("do_#{tag}")
86 | rescue
87 | # can't find pre-processing func for a tag.
88 | end
89 | end
90 |
91 |
92 | ##
93 | ## Per record processing
94 | ##
95 |
96 | # If there are entries contain http://, quote it.
97 | # If there are entries contain JapanesAuthorMaps, use it as author name mapping
98 | def do_rec_note_prep(l, r)
99 | if r =~ /JapaneseAuthorsMap:\s*(.*)\},/
100 | mapstr = $1
101 | maplist = mapstr.split(/,\s*/)
102 | maplist.each do |a|
103 | e, j = a.split(/:\s*/)
104 | add_author_map(e, j)
105 | f, l = e.split(/\s+/)
106 | add_author_map("#{l}, #{f}", j)
107 | end
108 | end
109 | [l, r]
110 | end
111 |
112 | # Paper's note appear sometimes as "annote" instead of "note". Map this.
113 | def do_rec_annote_prep(l, r)
114 | do_rec_note_prep(l, r)
115 | [l, r]
116 | end
117 |
118 | #
119 |
120 | def do_rec_note(l, r)
121 | r.gsub!(/{(http:\/\/\S+)(.*)}/, '{\\url{\1}\2}')
122 | r.gsub!(/JapaneseAuthorsMap:\s*(.*)\},/, "},")
123 | [l, r]
124 | end
125 |
126 | # Paper's note appear sometimes as "annote" instead of "note". Map this.
127 | def do_rec_annote(l, r)
128 | l.sub!(/annote/, "note")
129 | do_rec_note(l, r)
130 | end
131 |
132 | def do_rec_uri(l, r)
133 | [l, r]
134 | end
135 |
136 | def do_rec_url(l, r)
137 | r.gsub!(/{(.*)}/, '{\\url\&}')
138 | [l, r]
139 | end
140 |
141 | def do_rec_title(l, r)
142 | r.gsub!(/\{.*\}/, '{\&}') # always quote it
143 | [l, r]
144 | end
145 |
146 | def do_rec_author(l, r)
147 | r.gsub!(@lib.keywords_re, '{\&}')
148 | r = apply_author_map(r)
149 | [l, r]
150 | end
151 |
152 | def do_rec_journal(l, r)
153 | r.gsub!(@lib.keywords_re, '{\&}')
154 | [l, r]
155 | end
156 |
157 | ## * article
158 | ##
159 | ## - Specification from LaTeX Companion:
160 | ##
161 | ## article An article from a journal or magazine.
162 | ##
163 | ## Required: author, title, journal, year.
164 | ## Optional: volume, number, pages, month, note
165 |
166 | def do_article
167 | # if it's a specification, force to @manual
168 | # if it's a site description, force to @misc
169 | # I couldn't determin condition whether output will be @manual or @article.
170 | if @article_type == :specification
171 | @lines.each {|l| l.sub!(/@article{/, "@manual{") }
172 | do_manual
173 | elsif @article_type == :site
174 | @lines.each {|l| l.sub!(/@article{/, "@misc{") }
175 | do_misc
176 | end
177 | end
178 |
179 | ## * manual
180 | def do_manual
181 | end
182 |
183 | ## * misc
184 | def do_misc
185 | end
186 |
187 | ## * inproceedings
188 | ##
189 | ## - Specification from LaTeX Companion:
190 | ##
191 | ## inproceedings An article in a conference proceedings.
192 | ##
193 | ## Required: author, title, booktitle, year.
194 | ## Optional: editor, volume or number, series, pages, address, month,
195 | ## organization, publisher, note.
196 | ##
197 | ##
198 | ## @inproceedings should use booktitle
199 | ##
200 | ## convert: journal -> booktitle
201 |
202 | def do_inproceedings
203 | @lines.each {|l| l.sub!(/journal =/, "booktitle =") }
204 | end
205 |
206 | # OTHERs, including but not limited to:
207 | # manual, masterthesis, misc, phdthesis, techreport
208 | # book, inbook
209 |
210 | end
211 |
212 | class PapersBibLibrary < BibLibrary
213 | def new_bib(tag, citekey, lines)
214 | PapersBibEntry.new(self, tag, citekey, lines)
215 | end
216 |
217 | def postread
218 | super
219 | end
220 |
221 | end
222 |
--------------------------------------------------------------------------------
/bib_library.rb:
--------------------------------------------------------------------------------
1 | # Bibliography entry handler
2 |
3 | require 'date'
4 | require 'digest/md5'
5 |
6 | class BibEntry < Array
7 | attr_reader :tag, :citekey, :lines, :bib_hash, :title, :author, :authors
8 | attr_reader :date_added, :date_processed, :date_modified
9 |
10 | def initialize(lib, tag, citekey, lines)
11 | @lib = lib
12 | @tag = tag
13 | @citekey = citekey
14 | @lines = lines
15 | @date_added = 0
16 | @date_modified = 0
17 | @date_processed = 0
18 | @bib_hash = 0
19 | @title = ""
20 | @author = ""
21 | end
22 |
23 | def prep
24 | # create digest of the object, ignoring date related fields
25 | @bib_hash = Digest::MD5.new
26 |
27 | # Parsing several dates and some important fields
28 | @lines ||= [ ]
29 | @lines.each do |line|
30 | if line =~ /\s*booktitle\s*=\s*{(.*)},?\s*$/
31 | @booktitle = $1
32 | @booktitle.gsub!(/\{*/, '')
33 | @booktitle.gsub!(/\},/, '')
34 | elsif line =~ /\s*title\s*=\s*{(.*)},?\s*$/
35 | @title = $1
36 | @title.gsub!(/\{/, '')
37 | @title.gsub!(/\}/, '')
38 | elsif line =~ /\s*author\s*=\s*{(.*)},?\s*$/
39 | @author = $1
40 | @author.gsub!(/\{/, '')
41 | @author.gsub!(/\}/, '')
42 | @authors = @author.split(/ and /)
43 | elsif line =~ /\s*date-(\S+)\s*=\s*{\s*(.+)\s*}/
44 | k, d = $1, DateTime.parse($2)
45 | if k == "added"
46 | @date_added = d
47 | elsif k == "modified"
48 | @date_modified = d
49 | elsif k == "written-via-bibdump"
50 | @date_processed = d
51 | end
52 | else
53 | @bib_hash.update(line)
54 | end
55 | end
56 | end
57 |
58 | def out(o)
59 | o.puts "% " + "-"*(76 - @citekey.size) + " #{@citekey}"
60 | o.puts ""
61 | @lines.each {|l| o.puts l}
62 | o.puts ""
63 | end
64 |
65 | def to_s
66 | @lines.join("")
67 | end
68 |
69 | def write_opt(opts)
70 | "w" + ( opts.has_key?(:output_encoding) ? ":"+opts[:output_encoding] : "")
71 | end
72 |
73 | def write_to_file(file, opts, marker_lines = [])
74 | STDERR.puts "Writing citation <#{@citekey}> to #{file}"
75 | open(file, write_opt(opts)) do |f|
76 | @lines.each {|l| f.puts l unless l =~ /^}\s*$/ } if @lines != nil
77 | marker_lines.each {|l| f.puts l }
78 | f.puts "}"
79 | end
80 | end
81 |
82 | # equality means: contents other than date fields match exactly
83 | def ==(x)
84 | if @bib_hash == x.bib_hash
85 | if @date_added == x.date_added and
86 | @date_modified == x.date_modified
87 | return true
88 | else
89 | end
90 | else
91 | STDERR.puts "<#{citekey}> Bib_Hash Mismatch: #{bib_hash} #{x.bib_hash}"
92 | return false
93 | end
94 |
95 | false
96 | end
97 |
98 | # Minimum sanity check here.
99 | # if it is not equal, there must be some date mismatch..
100 | # returns if paper's entry is newer then locally edited bibs,
101 | # added > modified, and date added should be same.
102 | def inconsistent?(x)
103 | if @date_added > @date_modified or
104 | x.date_added > x.date_modified
105 | # @date_added != x.date_added or
106 | # @date_modified > x.date_modified
107 |
108 | return true
109 | end
110 | false
111 | end
112 |
113 | end
114 |
115 | class BibLibrary < Hash
116 | attr_reader :keywords_re
117 |
118 | def read_opt(opts)
119 | "r" + ( opts.has_key?(:encoding) ? ":"+opts[:encoding] : "")
120 | end
121 |
122 | def initialize(file = nil, opts)
123 | @opts = opts
124 | ropt = read_opt(opts)
125 | if file != nil
126 | kfile = opts[:key_file]
127 | if kfile == ""
128 | kfile = File.basename(file, ".bib")+"-keywords.txt"
129 | end
130 | if File.file?(kfile)
131 | read_key(kfile, ropt)
132 | end
133 | read(file, ropt)
134 | end
135 | end
136 |
137 | def read_key(file, encoding = "r")
138 | @keywords = Array.new
139 | open(file, encoding) do |f|
140 | f.each do |l|
141 | l.chomp!
142 | unless l =~ /^\s*(#|%)/ || l =~ /^\s*$/
143 | @keywords.push(l)
144 | end
145 | end
146 | end
147 | @keywords_re = Regexp.new("("+@keywords.join("|")+")")
148 | end
149 |
150 | def read(file, read_opt)
151 | lines = nil
152 | inside = false
153 | tag = "?"
154 | citekey = "?"
155 |
156 | begin
157 | open(bibpathnormalize(file), read_opt) do |f|
158 | f.each do |l|
159 | if l =~ /^%/
160 | # comments
161 | elsif l =~ /^\s+$/
162 | # blank lines
163 | else
164 | if l =~ /^\@([A-Za-z]+)\{([^,]+),$/
165 | tag, citekey = $1, $2
166 | lines = [ ]
167 | inside = true
168 | end
169 |
170 | lines.push(l) if inside
171 |
172 | if l =~ /^\}$/
173 | self[citekey] = new_bib(tag, citekey, lines)
174 | lines = nil
175 | inside = false
176 | end
177 | end
178 | end
179 | end
180 | rescue ArgumentError
181 | STDERR.puts "invalid byte sequence in #{file}"
182 | exit 1
183 | end
184 | postread if self.size != 0
185 | end
186 |
187 | def new_bib(tag, citekey, lines)
188 | BibEntry.new(self,tag, citekey, lines)
189 | end
190 |
191 | def postread
192 | prep
193 | end
194 |
195 | def prep
196 | each {|k, v| v.prep }
197 | end
198 |
199 | def out(o)
200 | keys.sort.each {|k| self[k].out(o) }
201 | end
202 |
203 | def to_s
204 | self.keys.sort.each{|k| self[k].to_s }.join("\n")
205 | end
206 |
207 |
208 | # Merge entire BibLibrary onto self
209 | def merge(xlib)
210 | xlib.each_value { |v| merge_one(v) }
211 | end
212 |
213 | # Merge one entry onto self
214 | def merge_one(n)
215 |
216 | # if the primary library (self) does not contain the entry, don't merge
217 | # unless --merge option given
218 | citekey = n.citekey
219 | unless self.has_key?(citekey) and @opts[:add] == false
220 | STDERR.puts "<#{n.citekey}> do not exist in primary bib file. use --add to force adding them"
221 | else
222 | old = self[citekey]
223 | if old.bib_hash == n.bib_hash # if contents is same.. don't replace
224 | STDERR.puts "Skiping: <#{citekey}>"
225 | elsif old.inconsistent?(n)
226 | STDERR.puts "Inconsistent: <#{citekey}>"
227 | if @opts[:dump_mismatch]
228 | STDERR.puts "----- #{old.bib_hash}"
229 | STDERR.puts old.to_s
230 | STDERR.puts "----- #{n.bib_hash}"
231 | STDERR.puts n.to_s
232 | end
233 | else
234 | if @opts[:dump_mismatch]
235 | STDERR.puts "----- #{old.bib_hash}"
236 | STDERR.puts old.to_s
237 | STDERR.puts "----- #{n.bib_hash}"
238 | STDERR.puts n.to_s
239 | end
240 |
241 | if @opts[:overwrite] # dont overwrite unless specified
242 | STDERR.puts "Replacing: <#{citekey}>"
243 | self[citekey] = n
244 | else
245 | STDERR.puts "*NOT* Replacing: <#{citekey}> -- use --overwrite to replace"
246 | end
247 | end
248 | end
249 | end
250 |
251 | def bibpathnormalize(c)
252 | c.gsub(/:/, "_")
253 | end
254 |
255 | def bibdirpath(s)
256 | @opts[:bib_dir] + "/" + s + ".bib"
257 | end
258 |
259 | def mkbibpath_1(c, suffix, wflag = false)
260 | return nil if c == nil
261 | fn = @opts[:bib_dir] + "/" + c + "." + suffix
262 | if wflag == nil
263 | return fn if File.file?(fn)
264 | else # only check for existing of directory
265 | return fn if File.directory?(File.dirname(fn))
266 | end
267 |
268 | return nil
269 | end
270 |
271 |
272 | def mkbibpath(c, wflag = false)
273 | s = bibpathnormalize(c)
274 | r = nil
275 | if @opts[:group] != nil
276 | p = mkbibpath_1(s, "#{@opts[:group]}-bib", wflag)
277 | if File.file?(p)
278 | STDERR.puts "Found: group <#{@opts[:group]}> variant for #{s}"
279 | r = p
280 | end
281 | end
282 | r = mkbibpath_1(s, "bib", wflag) if r == nil
283 | return r
284 | end
285 |
286 | end
287 |
--------------------------------------------------------------------------------