+++ /dev/null
-(* Copyright (C) 2002-2005, HELM Team.
- *
- * This file is part of HELM, an Hypertextual, Electronic
- * Library of Mathematics, developed at the Computer Science
- * Department, University of Bologna, Italy.
- *
- * HELM is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * HELM 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 for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with HELM; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- *
- * For details, see the HELM World-Wide-Web page,
- * http://cs.unibo.it/helm/.
- *)
-
-open Printf
-
-let debug = true
-let debug_print s = if debug then prerr_endline s
-let _ = Http_common.debug := false
-
-exception Chat_unfinished
-exception Unbound_identifier of string
-exception Invalid_action of string (* invalid action for "/search" method *)
-
- (** raised by elim when a MutInd is required but not found *)
-exception Not_a_MutInd
-
-let daemon_name = "Whelp"
-let configuration_file = "/projects/helm/etc/whelp.conf.xml"
-
-let placeholders = [
- "ACTION"; "ADVANCED"; "ADVANCED_CHECKED"; "CHOICES"; "CURRENT_CHOICES";
- "EXPRESSION"; "ID"; "IDEN"; "ID_TO_URIS"; "INTERPRETATIONS";
- "INTERPRETATIONS_LABELS"; "MSG"; "NEW_ALIASES"; "NEXT_LINK"; "NO_CHOICES";
- "PAGE"; "PAGES"; "PAGELIST"; "PREV_LINK"; "QUERY_KIND"; "QUERY_SUMMARY"; "RESULTS";
- "SEARCH_ENGINE_URL"; "SIMPLE_CHECKED"; "TITLE";
-]
-
-let tag =
- let regexps = Hashtbl.create 25 in
- List.iter
- (fun tag -> Hashtbl.add regexps tag (Pcre.regexp (sprintf "@%s@" tag)))
- placeholders;
- fun name ->
- try
- Hashtbl.find regexps name
- with Not_found -> assert false
-
- (* First of all we load the configuration *)
-let _ = Helm_registry.load_from configuration_file
-let port = Helm_registry.get_int "search_engine.port"
-let pages_dir = Helm_registry.get "search_engine.html_dir"
-
-let moogle_TPL = pages_dir ^ "/moogle.html"
-let choices_TPL = pages_dir ^ "/moogle_chat.html"
-
-let my_own_url =
- let ic = Unix.open_process_in "hostname -f" in
- let hostname = input_line ic in
- ignore (Unix.close_process_in ic);
- sprintf "http://%s:%d" hostname port
-let _ = Helm_registry.set "search_engine.my_own_url" my_own_url
-
-let bad_request body outchan =
- Http_daemon.respond_error ~code:(`Status (`Client_error `Bad_request)) ~body
- outchan
-
- (** chain application of Pcre substitutions *)
-let rec apply_substs substs line =
- match substs with
- | [] -> line
- | (rex, templ) :: rest -> apply_substs rest (Pcre.replace ~rex ~templ line)
- (** fold like function on files *)
-let fold_file f init fname =
- let inchan = open_in fname in
- let rec fold_lines' value =
- try
- let line = input_line inchan in
- fold_lines' (f value line)
- with End_of_file -> value
- in
- let res = (try fold_lines' init with e -> (close_in inchan; raise e)) in
- close_in inchan;
- res
- (** iter like function on files *)
-let iter_file f = fold_file (fun _ line -> f line) ()
-let javascript_quote s =
- let rex = Pcre.regexp "'" in
- let rex' = Pcre.regexp "\"" in
- Pcre.replace ~rex ~templ:"\\'"
- (Pcre.replace ~rex:rex' ~templ:"\\\"" s)
-let string_tail s =
- let len = String.length s in
- String.sub s 1 (len-1)
-let nonvar uri =
- let s = UriManager.string_of_uri uri in
- let len = String.length s in
- let suffix = String.sub s (len-4) 4 in
- not (suffix = ".var")
-
-let add_param_substs params =
- List.map
- (fun (key,value) ->
- let key' = (Pcre.extract ~pat:"param\\.(.*)" key).(1) in
- Pcre.regexp ("@" ^ key' ^ "@"), value)
- (List.filter
- (fun ((key,_) as p) -> Pcre.pmatch ~pat:"^param\\." key)
- params)
-
-let page_RE = Pcre.regexp "¶m\\.page=\\d+"
-let identifier_RE = Pcre.regexp "^\\s*(\\w|')+\\s*$"
-let qualified_mutind_RE =
- Pcre.regexp "^\\s*cic:(/(\\w|')+)+\\.ind#xpointer\\(1/\\d+\\)\\s*$"
-
-let query_kind_of_req (req: Http_types.request) =
- match req#path with
- | "/match" -> "Match"
- | "/hint" -> "Hint"
- | "/locate" -> "Locate"
- | "/elim" -> "Elim"
- | "/instance" -> "Instance"
- | _ -> ""
-
- (* given a uri with a query part in input try to find in it a string
- * "¶m_name=..." (where param_name is given). If found its value will be
- * set to param_value. If not, a trailing "¶m_name=param_value" (where
- * both are given) is added to the input string *)
-let patch_param param_name param_value url =
- let rex = Pcre.regexp (sprintf "&%s=[^&]*" (Pcre.quote param_name)) in
- if Pcre.pmatch ~rex url then
- Pcre.replace ~rex ~templ:(sprintf "%s=%s" param_name param_value) url
- else
- sprintf "%s&%s=%s" url param_name param_value
-
- (** HTML encoding, e.g.: "<" -> "<" *)
-let html_encode = Netencoding.Html.encode_from_latin1
-
-let fold_n_to_m f n m acc =
- let rec aux acc =
- function
- i when i <= m -> aux (f i acc) (i + 1)
- | _ -> acc
- in
- aux acc n
-
-let send_results results
- ?(id_to_uris = DisambiguateTypes.empty_environment)
- (req: Http_types.request) outchan
- =
- let query_kind = query_kind_of_req req in
- let interp = try req#param "interp" with Http_types.Param_not_found _ -> "" in
- let page_link anchor page =
- try
- let this = req#param "this" in
- let target =
- (patch_param "param.interp" interp
- (patch_param "param.page" (string_of_int page)
- this))
- in
- let target = Pcre.replace ~pat:"&" ~templ:"&" target in
- sprintf "<a href=\"%s\">%s</a>" target anchor
- with Http_types.Param_not_found _ -> ""
- in
- Http_daemon.send_basic_headers ~code:(`Code 200) outchan ;
- Http_daemon.send_header "Content-Type" "text/xml" outchan;
- Http_daemon.send_CRLF outchan ;
- let subst =
- match results with
- | `Results results ->
- let page = try int_of_string (req#param "page") with _ -> 1 in
- let results_no = List.length results in
- let results_per_page =
- Helm_registry.get_int "search_engine.results_per_page"
- in
- let pages =
- if results_no mod results_per_page = 0 then
- results_no / results_per_page
- else
- results_no / results_per_page + 1
- in
- let pages = if pages = 0 then 1 else pages in
- let additional_pages = 3 in
- let (summary, results) = MooglePp.theory_of_result page results in
- [ tag "PAGE", string_of_int page;
- tag "PAGES", string_of_int pages ^ " Pages";
- tag "PAGELIST",
- (let inf = page - additional_pages in
- let sup = page + additional_pages in
- let superinf = inf - (sup - pages) in
- let supersup = sup + (1 - inf) in
- let n,m =
- if inf >= 1 && sup <= pages then
- inf,sup
- else if inf < 1 then
- 1, (if supersup <= pages then supersup else pages)
- else (* sup > pages *)
- (if superinf >= 1 then superinf else 1),pages
- in
- fold_n_to_m
- (fun n acc -> acc ^ " " ^
- (if n = page then string_of_int n
- else page_link (string_of_int n) n))
- n m "");
- tag "PREV_LINK", (if page > 1 then page_link "Prev" (page-1) else "");
- tag "NEXT_LINK",
- (if page < pages then page_link "Next" (page+1) else "");
- tag "QUERY_KIND", query_kind;
- tag "QUERY_SUMMARY", summary;
- tag "RESULTS", results ]
- | `Error msg ->
- [ tag "PAGE", "1";
- tag "PAGES", "1 Page";
- tag "PAGELIST", "";
- tag "PREV_LINK", "";
- tag "NEXT_LINK", "";
- tag "QUERY_KIND", query_kind;
- tag "QUERY_SUMMARY", "error";
- tag "RESULTS", msg ]
- in
- let advanced =
- try
- req#param "advanced"
- with Http_types.Param_not_found _ -> "no"
- in
- let subst =
- (tag "SEARCH_ENGINE_URL", my_own_url) ::
- (tag "ADVANCED", advanced) ::
- (tag "EXPRESSION", html_encode (req#param "expression")) ::
- add_param_substs req#params @
- (if advanced = "no" then
- [ tag "SIMPLE_CHECKED", "checked='true'";
- tag "ADVANCED_CHECKED", "" ]
- else
- [ tag "SIMPLE_CHECKED", "";
- tag "ADVANCED_CHECKED", "checked='true'" ]) @
- subst
- in
- iter_file
- (fun line ->
- let new_aliases = DisambiguatePp.pp_environment id_to_uris in
- let processed_line =
- apply_substs
- (* CSC: Bug here: this is a string, not an array! *)
- ((tag "NEW_ALIASES", "'" ^ javascript_quote new_aliases ^ "'") ::
- subst)
- line
- in
- output_string outchan (processed_line ^ "\n"))
- moogle_TPL
-
-let exec_action dbd (req: Http_types.request) outchan =
- let term_str = req#param "expression" in
- try
- if req#path = "/elim" &&
- not (Pcre.pmatch ~rex:identifier_RE term_str ||
- Pcre.pmatch ~rex:qualified_mutind_RE term_str) then
- raise Not_a_MutInd;
- let (context, metasenv) = ([], []) in
- let id_to_uris_raw =
- try req#param "aliases"
- with Http_types.Param_not_found _ -> ""
- in
- let parse_interpretation_choices choices =
- List.map int_of_string (Pcre.split ~pat:" " choices) in
- let parse_choices choices_raw =
- let choices = Pcre.split ~pat:";" choices_raw in
- List.fold_left
- (fun f x ->
- match Pcre.split ~pat:"\\s" x with
- | ""::id::tail
- | id::tail when id<>"" ->
- (fun id' ->
- if id = id' then
- Some (List.map
- (fun u -> UriManager.uri_of_string
- (Netencoding.Url.decode u))
- tail)
- else
- f id')
- | _ -> failwith "Can't parse choices")
- (fun _ -> None)
- choices
- in
- let id_to_uris = DisambiguatePp.parse_environment id_to_uris_raw in
- let id_to_choices =
- try
- parse_choices (req#param "choices")
- with Http_types.Param_not_found _ -> (fun _ -> None)
- in
- let interpretation_choices =
- try
- let choices_raw = req#param "interpretation_choices" in
- if choices_raw = "" then None
- else Some (parse_interpretation_choices choices_raw)
- with Http_types.Param_not_found _ -> None
- in
- let module Chat: DisambiguateTypes.Callbacks =
- struct
- let interactive_user_uri_choice ~selection_mode ?ok
- ?enable_button_for_non_vars ~(title: string) ~(msg: string)
- ~(id: string) (choices: UriManager.uri list)
- =
- match id_to_choices id with
- | Some choices -> choices
- | None -> List.filter nonvar choices
-
- let interactive_interpretation_choice interpretations =
- match interpretation_choices with
- | Some l -> l
- | None ->
- let html_interpretations =
- MooglePp.html_of_interpretations interpretations
- in
- Http_daemon.send_basic_headers ~code:(`Code 200) outchan ;
- Http_daemon.send_CRLF outchan ;
- let advanced =
- try
- req#param "advanced"
- with Http_types.Param_not_found _ -> "no"
- in
- let query_kind = query_kind_of_req req in
- iter_file
- (fun line ->
- let processed_line =
- apply_substs
- [ tag "SEARCH_ENGINE_URL", my_own_url;
- tag "ADVANCED", advanced;
- tag "INTERPRETATIONS", html_interpretations;
- tag "CURRENT_CHOICES", req#param "choices";
- tag "EXPRESSION", html_encode (req#param "expression");
- tag "QUERY_KIND", query_kind;
- tag "QUERY_SUMMARY", "disambiguation";
- tag "ACTION", string_tail req#path ]
- line
- in
- output_string outchan (processed_line ^ "\n"))
- choices_TPL;
- raise Chat_unfinished
-
- let input_or_locate_uri ~title ?id () =
- match id with
- | Some id -> raise (Unbound_identifier id)
- | None -> assert false
- end
- in
- let module Disambiguate' = Disambiguate.Make(Chat) in
- let ast = Grammar.Entry.parse CicNotationParser.term (Stream.of_string term_str) in
- let (id_to_uris, metasenv, term) =
- match
- Disambiguate'.disambiguate_term ~dbd ~context ~metasenv
- ~aliases:id_to_uris ast
- with
- | [id_to_uris,metasenv,term,_] -> id_to_uris,metasenv,term
- | _ -> assert false
- in
- let uris =
- match req#path with
- | "/match" -> MetadataQuery.match_term ~dbd term
- | "/instance" -> MetadataQuery.instance ~dbd term
- | "/hint" ->
- let status = ProofEngineTypes.initial_status term metasenv in
- let intros = PrimitiveTactics.intros_tac () in
- let subgoals = ProofEngineTypes.apply_tactic intros status in
- (match subgoals with
- | proof, [goal] ->
- let (uri,metasenv,bo,ty) = proof in
- List.map fst (MetadataQuery.experimental_hint ~dbd (proof, goal))
- | _ -> assert false)
- | "/elim" ->
- let uri =
- match term with
- | Cic.MutInd (uri, typeno, _) ->
- UriManager.uri_of_uriref uri typeno None
- | _ -> raise Not_a_MutInd
- in
- MetadataQuery.elim ~dbd uri
- | _ -> assert false
- in
- let uris = List.map UriManager.string_of_uri uris in
- send_results ~id_to_uris (`Results uris) req outchan
- with
- | Not_a_MutInd ->
- send_results (`Error (MooglePp.pp_error "Not an inductive type"
- ("elim requires as input an identifier corresponding to an inductive"
- ^ " type")))
- req outchan
-
-let callback dbd (req: Http_types.request) outchan =
- try
- debug_print (sprintf "Received request: %s" req#path);
- (match req#path with
- | "/getpage" ->
- (* TODO implement "is_permitted" *)
- (let is_permitted page = not (Pcre.pmatch ~pat:"/" page) in
- let page = req#param "url" in
- let fname = sprintf "%s/%s" pages_dir page in
- let preprocess =
- (try
- bool_of_string (req#param "preprocess")
- with Invalid_argument _ | Http_types.Param_not_found _ -> false)
- in
- (match page with
- | page when is_permitted page && Sys.file_exists fname ->
- Http_daemon.send_basic_headers ~code:(`Code 200) outchan;
- Http_daemon.send_header "Content-Type" "text/html" outchan;
- Http_daemon.send_CRLF outchan;
- if preprocess then begin
- iter_file
- (fun line ->
- output_string outchan
- ((apply_substs
- ((tag "SEARCH_ENGINE_URL", my_own_url) ::
- (tag "ADVANCED", "no") ::
- (tag "RESULTS", "") ::
- add_param_substs req#params)
- line) ^
- "\n"))
- fname
- end else
- Http_daemon.send_file ~src:(Http_types.FileSrc fname) outchan
- | page -> Http_daemon.respond_forbidden ~url:page outchan))
- | "/help" -> Http_daemon.respond ~body:daemon_name outchan
- | "/locate" ->
- let initial_expression =
- try req#param "expression" with Http_types.Param_not_found _ -> ""
- in
- let expression =
- Pcre.replace ~pat:"\\s*$"
- (Pcre.replace ~pat:"^\\s*" initial_expression)
- in
- if expression = "" then
- send_results (`Results []) req outchan
- else begin
- let results = MetadataQuery.locate ~dbd expression in
- let results = List.map UriManager.string_of_uri results in
- send_results (`Results results) req outchan
- end
- | "/hint"
- | "/elim"
- | "/instance"
- | "/match" -> exec_action dbd req outchan
- | invalid_request ->
- Http_daemon.respond_error ~code:(`Status (`Client_error `Bad_request))
- outchan);
- debug_print (sprintf "%s done!" req#path)
- with
- | Chat_unfinished -> ()
- | Http_types.Param_not_found attr_name ->
- bad_request (sprintf "Parameter '%s' is missing" attr_name) outchan
- | CicNotationParser.Parse_error (_, msg) ->
- send_results (`Error (MooglePp.pp_error "Parse error" msg)) req outchan
- | Unbound_identifier id ->
- send_results (`Error (MooglePp.pp_error "Unbound identifier" id)) req
- outchan
- | exn ->
- let exn_string = Printexc.to_string exn in
- debug_print exn_string;
- let msg = MooglePp.pp_error "Uncaught exception" exn_string in
- send_results (`Error msg) req outchan
-
-let restore_environment () =
- match
- Helm_registry.get_opt Helm_registry.string "search_engine.environment_dump"
- with
- | None -> ()
- | Some fname ->
- printf "Restoring Cic environment from %s ... " fname; flush stdout;
- let ic = open_in fname in
- CicEnvironment.restore_from_channel ic;
- close_in ic;
- printf "done!\n"; flush stdout
-
-let read_notation () =
- CicNotation.load_notation (Helm_registry.get "search_engine.notations");
- CicNotation.load_notation (Helm_registry.get "search_engine.interpretations")
-
-let _ =
- printf "%s started and listening on port %d\n" daemon_name port;
- printf "Current directory is %s\n" (Sys.getcwd ());
- printf "HTML directory is %s\n" pages_dir;
- flush stdout;
- Unix.putenv "http_proxy" "";
- let dbd =
- Mysql.quick_connect
- ~host:(Helm_registry.get "db.host")
- ~database:(Helm_registry.get "db.database")
- ~user:(Helm_registry.get "db.user")
- ()
- in
- restore_environment ();
- read_notation ();
- Http_daemon.start' ~port (callback dbd);
- printf "%s is terminating, bye!\n" daemon_name
-