Source

footnotes-ocaml / footnotes.ml

(* compile with: ocamlopt -o footnotes str.cmxa footnotes.ml *)

let sep_line = "@footnote:"		(* the seperator line *)

let ref_regexp = Str.regexp "\\[\\([0-9]+\\)\\]"

let ref_of_int n = "[" ^ string_of_int n ^ "]"

let output_endline c s = output_string c s; output_char c '\n'

(* read the body from inc, substitute the references from table refs *)
(* if ref is not in refs, evaluate unknown_ref; print to outc *)
let rec process_body inc outc refs unknown_ref =
  let l = input_line inc in
  if l = sep_line then output_endline outc sep_line else
  let sub_ref _ =
    let old_n = int_of_string (Str.matched_group 1 l) in
    try ref_of_int (Hashtbl.find refs old_n)
    with Not_found -> unknown_ref old_n in
  output_endline outc (Str.global_substitute ref_regexp sub_ref l);
  process_body inc outc refs unknown_ref

(* read the foots from inc, process the lines with process_foot *)
let rec process_foots inc process_foot malformed_foot =
  let lo = try Some (input_line inc) with End_of_file -> None in
  match lo with
    None   -> ()
  | Some l ->
      if Str.string_match ref_regexp l 0 then
	process_foot l (int_of_string (Str.matched_group 1 l))
      else malformed_foot l;
      process_foots inc process_foot malformed_foot

(* insert a new reference for old_n into refs *)
let insert_ref refs old_n =
  Hashtbl.add refs old_n (Hashtbl.length refs + 1)

(* renumber and reorder the footnotes by their appearance in the body *)
let renumber_by_body inc outc =
  let refs = Hashtbl.create 1000000 in
  let new_ref old_n =
    insert_ref refs old_n;
    ref_of_int (Hashtbl.find refs old_n) in
  process_body inc outc refs new_ref;
  let foots = Array.make (Hashtbl.length refs) None in
  let add_foot l old_n =
    try
      let n = Hashtbl.find refs old_n in
      foots.(n-1) <- Some (Str.string_after l (Str.match_end ()))
    with
      Not_found -> prerr_endline ("unreferenced footnote: " ^ l)
  and prerr_malformed l = prerr_endline ("malformed footnote: " ^ l) in
  process_foots inc add_foot prerr_malformed;
  let print_foot i foot =
    output_string outc (ref_of_int (i + 1));
    match foot with
      None   -> output_endline outc " ### missing footnote ###"
    | Some l -> output_endline outc l in
  Array.iteri print_foot foots

(* renumber the footnote references by the original footnote order *)
let renumber_by_foots inc outc =
  while input_line inc <> sep_line do () done; (* skip the body *)
  let refs = Hashtbl.create 1000000 in
  let add_foot_ref _ old_n =
    if not (Hashtbl.mem refs old_n) then insert_ref refs old_n in
  process_foots inc add_foot_ref ignore;
  seek_in inc 0;			(* back to beginning *)
  process_body inc outc refs (function _ -> "[?]");
  let print_foot l old_n =
    let n = Hashtbl.find refs old_n in
    output_string outc (ref_of_int n);
    output_endline outc (Str.string_after l (Str.match_end ())) in
  process_foots inc print_foot (output_endline outc)

(* process options and files given on command line or stdin *)
let main () =
  if Array.length Sys.argv > 1 then
    let by_foots = ref false in
    let do_file f =
      let renumber =
	if !by_foots then renumber_by_foots else renumber_by_body
      and c = open_in f in
      (try renumber c stdout with e -> close_in c; raise e);
      close_in c in
    Arg.parse [("-f", Arg.Set by_foots,
		"Renumber footnotes by their original order")]
      do_file "Usage: footnotes [OPTION...] [file...]"
  else renumber_by_body stdin stdout;;

main ()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.