forked from backtracking/bibtex2html
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbib2bib.ml
315 lines (281 loc) · 9.23 KB
/
bib2bib.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
(**************************************************************************)
(* bibtex2html - A BibTeX to HTML translator *)
(* Copyright (C) 1997-2014 Jean-Christophe Filliâtre and Claude Marché *)
(* *)
(* This software is free software; you can redistribute it and/or *)
(* modify it under the terms of the GNU General Public *)
(* License version 2, as published by the Free Software Foundation. *)
(* *)
(* This software is distributed in the hope that it will be useful, *)
(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *)
(* *)
(* See the GNU General Public License version 2 for more details *)
(* (enclosed in the file GPL). *)
(**************************************************************************)
open Printf
open Bibtex
(* command-line arguments *)
let input_file_names = ref ([] : string list)
let bib_output_file_name = ref ""
let php_output_file_name = ref ""
let cite_output_file_name = ref ""
let get_input_file_name f =
input_file_names := f :: !input_file_names
let condition = ref Condition.True
let warn_condition = ref Condition.True
let add_condition rc c =
try
let c = Parse_condition.condition c in
rc := if !rc = Condition.True then c
else Condition.And(!rc,c)
with
Condition_lexer.Lex_error msg ->
prerr_endline ("Lexical error in condition: "^msg);
exit 1
| Parsing.Parse_error ->
prerr_endline "Syntax error in condition";
exit 1
let expand_abbrevs = ref false
let expand_xrefs = ref false
let no_comment = ref false
let sort_criteria = ref []
let reverse_sort = ref false
let remove_fields = ref []
let rename_field = ref ""
let rename_fields = ref []
let args_spec =
[
("-ob", Arg.String (fun f -> bib_output_file_name := f),
"<f> uses <f> as name for output bibliography file");
("-oc", Arg.String (fun f -> cite_output_file_name := f),
"<f> uses <f> as name for output citations file");
("--php-output", Arg.String (fun f -> php_output_file_name := f),
"<f> outputs resulting bibliography in PHP syntax in file <f>");
("-c", Arg.String (add_condition condition),
"<c> adds <c> as a filter condition");
("-wc", Arg.String (add_condition warn_condition),
"<c> generates a warning for each entry that does not satisfy condition \
<c>");
("-w", Arg.Set Options.warn_error, "stop on warning");
("--warn-error", Arg.Set Options.warn_error, "stop on warning");
("-d", Arg.Set Options.debug, "debug flag");
("-q", Arg.Set Options.quiet, "quiet flag");
("--quiet", Arg.Set Options.quiet, "quiet flag");
("-s", Arg.String
(fun s -> sort_criteria := (String.lowercase_ascii s):: !sort_criteria),
"<c> sort with respect to keys (if c=$key) or a given field <c>");
("-r", Arg.Set reverse_sort,
"reverse the sort order");
("--no-comment", Arg.Unit (fun () -> no_comment := true),
"do not add extra comments at beginning");
("--remove", Arg.String
(fun s -> remove_fields := (String.lowercase_ascii s):: !remove_fields),
"<f> removes the field <f>");
("--rename",
Arg.Tuple
[ Arg.Set_string rename_field ;
Arg.String (fun s -> rename_fields :=
(String.lowercase_ascii !rename_field,
String.lowercase_ascii s):: !rename_fields)],
"<f1> <f2> rename field <f1> into <f2>");
("--expand", Arg.Unit (fun () -> expand_abbrevs := true),
"expand the abbreviations");
("--expand-xrefs", Arg.Unit (fun () -> expand_xrefs := true),
"expand the cross-references");
("--version", Arg.Unit (fun () -> Copying.banner "bib2bib"; exit 0),
"print version and exit");
("--warranty",
Arg.Unit (fun () -> Copying.banner "bib2bib"; Copying.copying(); exit 0),
"display software warranty")
]
let output_cite_file keys =
if !cite_output_file_name = "" then
prerr_endline "No citation file output (no file name specified)"
else
try
let ch = open_out !cite_output_file_name in
KeySet.iter (fun k -> output_string ch (k ^ "\n")) keys;
close_out ch
with
Sys_error msg ->
prerr_endline ("Cannot write output citations file (" ^ msg ^ ")");
exit 1
let output_bib_file remove rename biblio keys =
try
let ch =
if !bib_output_file_name = ""
then stdout
else open_out !bib_output_file_name
in
let cmd =
List.fold_right
(fun s t ->
if String.contains s ' '
then
if String.contains s '\''
then " \"" ^ s ^ "\"" ^ t
else " '" ^ s ^ "'" ^ t
else " " ^ s ^ t)
(Array.to_list Sys.argv)
""
in
let comments =
if !no_comment then empty_biblio else
add_new_entry
(Comment ("Command line:" ^ cmd))
(add_new_entry
(Comment
("This file has been generated by bib2bib "
^ Version.version))
empty_biblio)
in
let biblio = merge_biblios comments biblio in
Biboutput.output_bib ~remove ~rename ~html:false ch biblio keys;
if !bib_output_file_name <> "" then close_out ch
with Sys_error msg ->
prerr_endline ("Cannot write output bib file (" ^ msg ^ ")");
exit 1
let output_php_file remove rename biblio keys =
if !php_output_file_name <> "" then
try
let ch = open_out !php_output_file_name in
output_string ch "<?php
$parsed_bibfile =
Array
(
";
Biboutput.output_bib ~remove ~rename ~html:false ~php:true ch biblio keys;
output_string ch "
);
?>
";
close_out ch
with Biboutput.Bad_input_for_php msg ->
eprintf "error while producing PHP output: %s\n" msg;
exit 2
let rec make_compare_fun db criteria c1 c2 =
match criteria with
| [] -> 0
| field :: rem ->
let comp =
match field with
| "$key" ->
begin
match (c1,c2) with
| (Abbrev(s1,_),Abbrev(s2,_))
| (Entry(_,s1,_),Entry(_,s2,_)) ->
compare s1 s2
| _ -> 0
end
| "$type" ->
begin
match (c1,c2) with
| (Entry(s1,_,_),Entry(s2,_,_)) ->
compare s1 s2
| _ -> 0
end
| "$date" ->
begin
match (c1,c2) with
| (Entry(s1,t1,l1),Entry(s2,t2,l2)) ->
Expand.date_compare db
(s1,t1,Expand.expand_fields l1)
(s2,t2,Expand.expand_fields l2)
| _ -> 0
end
| f ->
begin
match (c1,c2) with
| (Entry(_,_,l1),Entry(_,_,l2)) ->
let s1 =
try
match List.assoc field l1 with
| [Bibtex.String(s)] -> s
| [Bibtex.Id(s)] -> s
| _ -> ""
with
Not_found -> ""
and s2 =
try
match List.assoc field l2 with
| [Bibtex.String(s)] -> s
| [Bibtex.Id(s)] -> s
| _ -> ""
with
Not_found -> ""
in
compare s1 s2
| _ -> 0
end
in
if comp = 0
then make_compare_fun db rem c1 c2
else
if !reverse_sort then -comp else comp
;;
let usage = "Usage: bib2bib [options] <input file names>\nOptions are:"
let main () =
Arg.parse args_spec get_input_file_name usage;
Copying.banner "bib2bib";
if !Options.debug then
begin
eprintf "command line:\n";
for i = 0 to pred (Array.length Sys.argv) do
eprintf "%s\n" Sys.argv.(i)
done;
end;
if !input_file_names = [] then input_file_names := [""];
if !Options.debug then begin
Condition.print !condition; printf "\n"
end;
let all_entries =
List.fold_right
(fun file accu ->
merge_biblios accu (Readbib.read_entries_from_file file))
!input_file_names
empty_biblio
in
let abbrv_expanded = Bibtex.expand_abbrevs all_entries in
let xref_expanded = Bibtex.expand_crossrefs abbrv_expanded in
let matching_keys =
Bibfilter.filter
xref_expanded
(fun e k f ->
if not (Condition.evaluate_cond e k f !warn_condition) then begin
if not !Options.quiet then
eprintf
"Warning: entry %s does not satisfy the input condition.\n"
k;
if !Options.warn_error then exit 2
end;
Condition.evaluate_cond e k f !condition)
in
if KeySet.cardinal matching_keys = 0 then
begin
eprintf "Warning: no matching reference found.\n";
if !Options.warn_error then exit 2;
end;
let user_expanded =
if !expand_abbrevs then
if !expand_xrefs then xref_expanded else abbrv_expanded
else
if !expand_xrefs then Bibtex.expand_crossrefs all_entries
else all_entries
in
let needed_keys = Bibfilter.saturate user_expanded matching_keys in
(* this should be to right place to sort the output bibliography *)
let final_bib =
if !sort_criteria = [] then user_expanded
else
let comp = make_compare_fun (Expand.expand user_expanded) (List.rev !sort_criteria) in
eprintf "Sorting...";
let b = Bibtex.sort comp user_expanded in
eprintf "done.\n";
b
in
output_cite_file matching_keys;
output_bib_file !remove_fields !rename_fields final_bib (Some needed_keys);
output_php_file !remove_fields !rename_fields final_bib (Some needed_keys)
let _ =
Printexc.catch main ()