(* * bibtex2html - A BibTeX to HTML translator * Copyright (C) 1997-2000 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). *) (*i $Id: main.ml,v 1.64 2006-11-02 11:58:41 filliatr Exp $ i*) (*s Main module of bibtex2html. *) open Printf open Translate (* Options. *) let excluded = ref ([] : string list) let add_exclude k = excluded := k :: !excluded let style = ref "plain" let command = ref "bibtex -min-crossrefs=1000" type sort = Unsorted | By_date | By_author let sort = ref Unsorted let reverse_sort = ref false let ignore_bibtex_errors = ref false let expand_abbrev_in_bib_output = ref true (* Optional citation file. *) let use_cite_file = ref false let citations = ref ([] : string list) let add_citations file = try let chan = open_in file and buf = Buffer.create 1024 in try while true do Buffer.add_char buf (input_char chan) done with End_of_file -> close_in chan; citations := (Str.split (Str.regexp "[ \t\n]+") (Buffer.contents buf)) @ !citations with Sys_error msg -> prerr_endline ("Cannot open citation file (" ^ msg ^ ")"); exit 1 (*s Sorting the entries. *) module KeyMap = Map.Make(struct type t = string let compare = compare end) let keep_combine combine l1 l2 = let map = List.fold_left (fun m ((_,k,_) as e) -> KeyMap.add k e m) KeyMap.empty l2 in let rec keep_rec = function | [] -> [] | ((_,k,_) as x)::rem -> if not (List.mem k !excluded) then try let y = KeyMap.find k map in (combine x y) :: (keep_rec rem) with Not_found -> keep_rec rem else keep_rec rem in keep_rec l1 let combine_f (c,_,b) e = c,b,e let rev_combine_f x y = combine_f y x let sort_entries entries bibitems = if not !Options.quiet then begin eprintf "Sorting..."; flush stderr end; let el = if !sort = By_author then keep_combine combine_f bibitems entries else keep_combine rev_combine_f entries bibitems in let sl = if !sort = By_date then Sort.list (fun (_,_,e1) (_,_,e2) -> Expand.date_order entries e1 e2) el else el in if not !Options.quiet then begin eprintf "ok.\n"; flush stderr end; if !reverse_sort then List.rev sl else sl (* We use BibTeX itself to format the entries. Operations: \begin{enumerate} \item create an auxiliary file tmp.aux \item call bibtex on it \item read the resulting tmp.bbl file to get the formatted entries \end{enumerate} *) let create_aux_file fbib tmp = let ch = open_out (tmp ^ ".aux") in output_string ch "\\relax\n\\bibstyle{"; output_string ch !style; output_string ch "}\n"; if !use_cite_file then List.iter (fun k -> output_string ch ("\\citation{" ^ k ^ "}\n")) !citations else output_string ch "\\citation{*}\n"; output_string ch "\\bibdata{"; output_string ch (Filename.chop_suffix fbib ".bib"); output_string ch "}\n"; close_out ch let rm f = try Sys.remove f with _ -> () let clean tmp = if not !Options.debug then begin rm (tmp ^ ".aux"); rm (tmp ^ ".blg"); rm (tmp ^ ".bbl"); rm tmp end let call_bibtex tmp = if not !Options.quiet then begin eprintf "calling BibTeX..."; flush stderr end; match let redir = if !output_file = "" || !Options.quiet then match Sys.os_type with | "Win32" -> "> nul 2>&1" | _ -> "> /dev/null 2>&1" else "" in Sys.command (sprintf "%s %s %s" !command tmp redir) with | 0 -> if not !Options.quiet then begin eprintf "\n"; flush stderr end | n -> if !ignore_bibtex_errors then begin if not !Options.quiet then begin eprintf "error %d (ignored)\n" n; flush stderr end end else begin eprintf "error %d while running bibtex\n" n; exit n end let read_one_biblio lb = let rec read_items acc lb = try let (_,k,_) as item = Bbl_lexer.bibitem lb in if !Options.debug then begin eprintf "[%s]" k; flush stderr end; read_items (item::acc) lb with Bbl_lexer.End_of_biblio -> List.rev acc in let name = Bbl_lexer.biblio_header lb in let items = read_items [] lb in (name,items) let read_biblios lb = let rec read acc lb = try let b = read_one_biblio lb in read (b::acc) lb with End_of_file -> List.rev acc in read [] lb let read_bbl tmp = let fbbl = tmp ^ ".bbl" in if not !Options.quiet then begin eprintf "Reading %s..." fbbl; flush stderr end; let ch = open_in fbbl in let lexbuf = Lexing.from_channel ch in let biblios = read_biblios lexbuf in close_in ch; clean tmp; if not !Options.quiet then begin eprintf "ok "; List.iter (fun (_,items) -> eprintf "(%d entries)" (List.length items)) biblios; eprintf "\n"; flush stderr end; biblios let get_biblios fbib = let tmp = Filename.temp_file "bib2html" "" in try create_aux_file fbib tmp; call_bibtex tmp; read_bbl tmp with e -> clean tmp; raise e (*i let insert_title_url bib = let rec remove_assoc x = function | [] -> raise Not_found | ((y,v) as p) :: l -> if x = y then (v,l) else let (v',l') = remove_assoc x l in (v', p :: l') in let url_value = function | [Bibtex.Id u] -> u | [Bibtex.String u] -> u | _ -> raise Not_found in let modify_entry f = try let t,f' = remove_assoc "TITLE" f in let u,f'' = remove_assoc "URL" f' in let u' = Html.normalize_url (url_value u) in let nt = (Bibtex.String (sprintf "\\begin{rawhtml}\\end{rawhtml}" u')) :: t @ [Bibtex.String "\\begin{rawhtml}\\end{rawhtml}"] in ("TITLE",nt) :: f'' with Not_found -> f in Bibtex.fold (fun com bib' -> match com with | Bibtex.Entry (ty,k,f) -> Bibtex.add_new_entry (Bibtex.Entry (ty,k,modify_entry f)) bib' | _ -> Bibtex.add_new_entry com bib') bib Bibtex.empty_biblio i*) let parse_only = ref false let print_keys = ref false let translate fullname = let input_bib = Readbib.read_entries_from_file fullname in if !parse_only then exit 0; let entries = List.rev (Expand.expand input_bib) in let biblios = if fullname = "" then begin let tmp = Filename.temp_file "bibtex2htmlinput" ".bib" in let ch = open_out tmp in Biboutput.output_bib ~html:false ch input_bib None; close_out ch; let bbl = get_biblios tmp in Sys.remove tmp; bbl end else get_biblios fullname in let sb = List.map (fun (name,bibitems) -> (name,sort_entries entries bibitems)) biblios in if !print_keys then begin List.iter (fun (_,bibitems) -> List.iter (fun (_,_,(_,k,_)) -> printf "%s\n" k) bibitems) sb; flush stdout; exit 0 end; format_list (if !expand_abbrev_in_bib_output then Bibtex.expand_abbrevs input_bib else input_bib) sb (if !use_cite_file then let keys = List.fold_right (fun s e -> Bibtex.KeySet.add s e) !citations Bibtex.KeySet.empty in let keys = List.fold_right (fun s e -> Bibtex.KeySet.remove s e) !excluded keys in Some (Bibfilter.saturate input_bib keys) else None) (*s Reading macros in a file. *) let read_macros f = let chan = open_in f in let lb = Lexing.from_channel chan in Latexscan.read_macros lb; close_in chan (*s Command line parsing. *) let usage ?(error=true) () = if error then prerr_endline "bibtex2html: bad command line syntax"; (if error then prerr_endline else print_endline) " Usage: bibtex2html [filename] -s style BibTeX style (plain, alpha, ...) -c command BibTeX command (otherwise bibtex is searched in your path) -d sort by date -a sort as BibTeX (usually by author) -u unsorted i.e. same order as in .bib file (default) -r reverse the sort -t title title of the HTML file (default is the filename) -bg color background color of the HTML file (default is none) -css file specify a style sheet file -o file redirect the output -footer additional footer in the HTML file -i ignore BibTeX errors -both produce versions with and without abstracts -multiple produce one file per entry -single produce a single page (with BibTeX input and output) -nodoc only produces the body of the HTML documents -nokeys do not print the BibTeX keys -nolinks do not print any web link -nobiblinks do not add web links in the BibTeX output -rawurl print URL instead of file type -heveaurl use HeVeA's \\url macro -noabstract do not print the abstracts (if any) -nokeywords do not print the keywords (if any) -nodoi do not insert the DOI links -doi-prefix url set the DOI links prefix (default is http://dx.doi.org/) -linebreak add a linebreak between an entry and its links -noheader do not print the header (bibtex2html command) -nofooter do not print the footer (bibtex2html web link) -noexpand do not expand abbreviations in the BibTeX output -nobibsource do not produce the BibTeX entries file -fsuffix give an alternate suffix for HTML files -lsuffix give an alternate suffix for HTML links -suffix s give an alternate suffix for HTML files and links -citefile f read keys to include from file f -e key exclude an entry -m file read (La)TeX macros in file -f field add a web link for that BibTeX field -nf field name add a web link for that BibTeX field, with the supplied name -note field declare a note field -dl use DL lists instead of TABLEs -labelname use the label name when inserting a link --print-keys print the sorted bibtex keys and exit -debug verbose mode (to find incorrect BibTeX entries) -q quiet mode -w stop on warning -v print version and exit On-line documentation at http://www.lri.fr/~filliatr/bibtex2html/ "; exit (if error then 1 else 0) let parse () = let rec parse_rec = function (* General aspect of the web page *) | ("-t" | "-title" | "--title") :: s :: rem -> title := s; title_spec := true; parse_rec rem | ("-t" | "-title" | "--title") :: [] -> usage() | ("-bg" | "-background" | "--background") :: s :: rem -> Html.bgcolor := Some s; parse_rec rem | ("-bg" | "-background" | "--background") :: [] -> usage() | ("-css" | "-style-sheet" | "--style-sheet") :: f :: rem -> Html.css := Some f; parse_rec rem | ("-css" | "-style-sheet" | "--style-sheet") :: [] -> usage() | ("-footer" | "--footer") :: s :: rem -> user_footer := s; parse_rec rem | ("-footer" | "--footer") :: [] -> usage() | ("-s" | "-style" | "--style") :: s :: rem -> style := s; parse_rec rem | ("-s" | "-style" | "--style") :: [] -> usage() | ("-noabstract" | "-no-abstract" | "--no-abstract") :: rem -> print_abstract := false; parse_rec rem | ("-nodoi" | "-no-doi" | "--no-doi") :: rem -> doi := false; parse_rec rem | ("-doi-prefix" | "--doi-prefix") :: s :: rem -> doi_prefix := s; parse_rec rem | ("-doi-prefix" | "--doi-prefix") :: [] -> usage () | ("-nokeywords" | "-no-keywords" | "--no-keywords") :: rem -> print_keywords := false; parse_rec rem | ("-nolinks" | "-no-links" | "--no-links") :: rem -> print_links := false; parse_rec rem | ("-nobiblinks" | "-no-bib-links" | "--no-bib-links") :: rem -> links_in_bib_file := false; parse_rec rem | ("-nokeys" | "-no-keys" | "--no-keys") :: rem -> nokeys := true; table := NoTable; parse_rec rem | ("-use-table" | "--use-table") :: rem -> table := Table; parse_rec rem | ("-usekeys" | "-use-keys" | "--use-keys") :: rem -> use_keys := true; parse_rec rem | ("-rawurl" | "-raw-url" | "--raw-url") :: rem -> raw_url := true; parse_rec rem (*i | ("-tu" | "-titleurl" | "--title-url") :: rem -> title_url := true; parse_rec rem i*) | ("-heveaurl" | "-hevea-url" | "--hevea-url") :: rem -> Latexscan.hevea_url := true; parse_rec rem | ("-linebreak" | "--linebreak") :: rem -> linebreak := true; parse_rec rem | ("-noheader" | "-no-header" | "--no-header") :: rem -> print_header := false; parse_rec rem | ("-nofooter" | "-no-footer" | "--no-footer") :: rem -> print_footer := false; parse_rec rem | ("-f" | "-field" | "--field") :: s :: rem -> add_field s; parse_rec rem | ("-f" | "-field" | "--field") :: [] -> usage() | ("-nf" | "-named-field" | "--named-field") :: s :: name :: rem -> add_named_field s name; parse_rec rem | ("-nf" | "-named-field" | "--named-field") :: ([_] | []) -> usage() | ("-note" | "--note") :: s :: rem -> add_note_field s; parse_rec rem | ("-note" | "--note") :: [] -> usage() | ("-ln" | "-labelname" | "--labelname" | "--label-name") :: rem -> use_label_name := true; parse_rec rem | ("-multiple" | "--multiple") :: rem -> multiple := true; parse_rec rem | ("-single" | "--single") :: rem -> multiple := false; both := false; print_keywords := false; bib_entries := false; single := true; parse_rec rem | ("-both" | "--both") :: rem -> both := true; parse_rec rem | ("-dl" | "--dl") :: rem -> table := DL; parse_rec rem (* Controlling the translation *) | ("-m" | "-macros-from" | "--macros-from") :: f :: rem -> read_macros f; parse_rec rem | ("-m" | "-macros-from" | "--macros-from") :: [] -> usage() (* Sorting the entries *) | ("-d" | "-sort-by-date" | "--sort-by-date") :: rem -> sort := By_date; parse_rec rem | ("-a" | "-sort-as-bibtex" | "--sort-as-bibtex") :: rem -> sort := By_author; parse_rec rem | ("-u" | "-unsorted" | "--unsorted") :: rem -> sort := Unsorted; parse_rec rem | ("-r" | "-reverse-sort" | "--reverse-sort") :: rem -> reverse_sort := true; parse_rec rem (* Options for selecting keys *) | ("-citefile" | "--citefile") :: f :: rem -> use_cite_file := true; add_citations f; parse_rec rem | ("-citefile" | "--citefile") :: [] -> usage() | ("-e" | "-exclude" | "--exclude") :: k :: rem -> add_exclude k; parse_rec rem | ("-e" | "-exclude" | "--exclude") :: [] -> usage() (* Miscellaneous options *) | ("-o" | "-output" | "--output") :: f :: rem -> output_file := f; parse_rec rem | ("-o" | "-output" | "--output") :: [] -> usage() | ("-nobibsource" | "--nobibsource") :: rem -> bib_entries := false; parse_rec rem | ("-nodoc" | "--nodoc" | "-no-doc" | "--no-doc") :: rem -> nodoc := true; parse_rec rem | ("-noexpand" | "-no-expand" | "--no-expand") :: rem -> expand_abbrev_in_bib_output := false; parse_rec rem | ("-i" | "-ignore-errors" | "--ignore-errors") :: rem -> ignore_bibtex_errors := true; parse_rec rem | ("-suffix" | "--suffix") :: s :: rem -> file_suffix := s; link_suffix := s; parse_rec rem | ("-fsuffix" | "-file-suffix" | "--file-suffix") :: s :: rem -> file_suffix := s; parse_rec rem | ("-lsuffix" | "-link-suffix" | "--link-suffix") :: s :: rem -> link_suffix := s; parse_rec rem | ("-suffix" | "--suffix" | "-fsuffix" | "--file-suffix" | "-file-suffix" | "-lsuffix" | "-link-suffix" | "--link-suffix") :: [] -> usage() | ("-c" | "-command" | "--command") :: s :: rem -> command := s; parse_rec rem | ("-c" | "-command" | "--command") :: [] -> usage() | ("-h" | "-help" | "-?" | "--help") :: rem -> usage ~error:false () | ("-v" | "-version" | "--version") :: _ -> Copying.banner "bibtex2html"; exit 0 | ("-warranty" | "--warranty") :: _ -> Copying.banner "bibtex2html"; Copying.copying(); exit 0 | ("-w" | "-warn-error" | "--warn-error") :: rem -> Options.warn_error := true; parse_rec rem | ("-q" | "-quiet" | "--quiet") :: rem -> Options.quiet := true; parse_rec rem | ("-debug" | "--debug") :: rem -> Options.debug := true; parse_rec rem | "-parse-only" :: rem -> parse_only := true; parse_rec rem | ("-print-keys" | "--print-keys") :: rem -> print_keys := true; parse_rec rem | [fbib] -> if not (Sys.file_exists fbib) then begin eprintf "%s: no such file\n" fbib; exit 1 end; let basename = Filename.basename fbib in if Filename.check_suffix basename ".bib" then (fbib, Filename.chop_suffix basename ".bib") else begin prerr_endline "bibtex2html: BibTeX file must have suffix .bib"; exit 1 end | [] -> ("","") | _ -> usage () in parse_rec (List.tl (Array.to_list Sys.argv)) (*s Main function. *) let main () = let (fbib,f) = parse () in Copying.banner "bibtex2html"; if fbib = "" then begin if not !title_spec then title := "bibtex2html output"; begin match !output_file with | "" -> bib_entries := false | "-" -> output_file := ""; bib_entries := false | _ -> () end end else begin input_file := f ^ ".bib"; begin match !output_file with | "" -> output_file := f; | "-" -> output_file := ""; bib_entries := false | _ -> () end; if not !title_spec then title := f end; Latexmacros.init_style_macros !style; (* producing the documents *) translate fbib let _ = Printexc.catch main ()