(***********************************************************************)
(*                                                                     *)
(*                             Active-DVI                              *)
(*                                                                     *)
(*                   Projet Cristal, INRIA Rocquencourt                *)
(*                                                                     *)
(*  Copyright 2002 Institut National de Recherche en Informatique et   *)
(*  en Automatique.  All rights reserved.  This file is distributed    *)
(*  under the terms of the GNU Lesser General Public License.          *)
(*                                                                     *)
(*  Jun Furuse, Didier Rémy and Pierre Weis.                           *)
(*  Contributions by Roberto Di Cosmo, Didier Le Botlan,               *)
(*  Xavier Leroy, and Alan Schmitt.                                    *)
(*                                                                     *)
(*  Based on Mldvi by Alexandre Miquel.                                *)
(***********************************************************************)

(* $Id: search.ml,v 1.15 2004/06/04 02:46:28 furuse Exp $ *)

let database_mtime = ref 0.0;;
let database_table = Hashtbl.create 257;;

let unwind_protect f x g y =
  try let v = f x in g y; v with
  | exc -> g y; raise exc;;

let open_process_in cmd =
  let (in_read, in_write) = Unix.pipe () in
  let pid =
    Unix.create_process "/bin/sh" [| "/bin/sh"; "-c"; cmd |]
      Unix.stdin in_write Unix.stderr in
  Unix.close in_write;
  let inchan = Unix.in_channel_of_descr in_read in
  pid, inchan;;

let close_process_in (pid, inchan) =
  close_in inchan;
  let rec wait () =
    try snd (Unix.waitpid [] pid) with
    | Unix.Unix_error(Unix.EINTR, _, _) -> wait () in
  wait ();;

exception Command;;

let command_string com opt =
  let command = Printf.sprintf "%s %s" com opt in
  Misc.debug_endline (Printf.sprintf "command_string: launching %s" command);
  try
    let pid, chan as pidchan = open_process_in command in
    unwind_protect input_line chan close_process_in pidchan 
  with
  | Unix.Unix_error (c, _, _) ->
      Misc.warning
        (Printf.sprintf "Error %s while executing %s %s"
          (Unix.error_message c) com opt);
      raise Command
  | End_of_file -> ""
;;

(* Add local path to search environment. *)
let addpath elem var kind =
  let oldv =
    try Unix.getenv var with
    | Not_found ->
        try command_string Config.kpsewhich_path
              (Printf.sprintf "-show-path='%s'" kind) with
        | Command -> "" in
  let newv = oldv ^ ":" ^ elem in
  Unix.putenv var newv
;;

addpath Config.advi_loc "PSHEADERS" Config.psheaders_kind;
addpath Config.advi_loc "TEXPICTS"  Config.texpicts_kind;;

let is_space = function
  | ' ' | '\n' | '\r' | '\t' -> true
  | _ -> false
;;

let remove_spaces line =
  let len = String.length line in
  let p = ref 0 and q = ref len in
  while !p < len && is_space line.[!p] do incr p done;
  while !q > !p && is_space line.[!q - 1] do decr q done;
  if !p = 0 && !q = len then line else
  String.sub line !p (!q - !p)
;;

let reload_database () =
  let ch = open_in Config.database_path in
  Hashtbl.clear database_table;
  let curr_dir = ref "" in
  try while true do
    let line = remove_spaces (input_line ch) in
    let len = String.length line in
    if len > 0 && line.[0] <> '%' then begin
      if line.[len - 1] = ':'
      then curr_dir := String.sub line 0 (len - 1)
      else Hashtbl.add database_table line (Filename.concat !curr_dir line)
    end
  done with
  | End_of_file -> close_in ch
  | e -> close_in ch; raise e
;;

let reload_if_changed_database () =
  try
    let stats = Unix.stat Config.database_path in
    let mtime = stats.Unix.st_mtime in
    if mtime > !database_mtime then begin
      database_mtime := mtime;
      reload_database ()
    end
  with _ -> ()
;;

let database_font_path fontname dpi =
  reload_if_changed_database ();
  let name = Printf.sprintf "%s.%dpk" fontname dpi in
  let path = Hashtbl.find database_table name in
  Filename.concat Config.texdir_path path
;;

let true_file_name options file =
  let args = String.concat " " (options @ [file]) in
  try
    let s = command_string Config.kpsewhich_path args in
    if s = "" then raise Command;
    s
  with
  | Command ->
      Misc.warning (Printf.sprintf "file %s is not found" file);
      raise Not_found
;;

let true_file_names options files =
  List.fold_right
    (fun file st ->
      try true_file_name options file :: st with Not_found -> st)
    files []
;;

let kpsewhich_font_path fontname dpi =
  match true_file_names ["-dpi=" ^ string_of_int dpi; "-mktex=pk"]
                        [fontname ^ ".pk"] with
  | name :: _ when name <> "" -> name
  | _ -> raise Not_found
;;

let unix_font_path fontname dpi =
  try database_font_path fontname dpi
  with _ -> kpsewhich_font_path fontname dpi
;;

let msdos_font_path fontname dpi =
  let filename = Printf.sprintf "%s.%dpk" fontname dpi in
  try Filename.concat (Sys.getenv "PKFONTS") filename
  with _ -> filename
;;

let font_path =
  match Sys.os_type with
  | "Unix" | "Cygwin" -> unix_font_path
  | "Win32" -> msdos_font_path
  | _ -> raise Not_found
;;
