X-Git-Url: http://matita.cs.unibo.it/gitweb/?a=blobdiff_plain;f=helm%2Focaml%2Fregistry%2Fhelm_registry.ml;h=8966bb9479b20f2b1dfe53013fb2da55b6499784;hb=dfdf5642131761008f0b0430594a83d45ab62789;hp=0c7b437238d1e57c23a9d5bcf73d83d6e4fde2b1;hpb=6556d39b592c1e56b0e7f4055242232b8bfe3c90;p=helm.git diff --git a/helm/ocaml/registry/helm_registry.ml b/helm/ocaml/registry/helm_registry.ml index 0c7b43723..8966bb947 100644 --- a/helm/ocaml/registry/helm_registry.ml +++ b/helm/ocaml/registry/helm_registry.ml @@ -29,6 +29,34 @@ let debug = false let debug_print s = if debug then prerr_endline ("Helm_registry debugging: " ^ s) + (** *) + +let list_uniq l = + let rec aux last_element = function + | [] -> [] + | hd :: tl -> + (match last_element with + | Some elt when elt = hd -> aux last_element tl + | _ -> hd :: aux (Some hd) tl) + in + aux None l + +let starts_with prefix = +(* + let rex = Str.regexp (Str.quote prefix) in + fun s -> Str.string_match rex s 0 +*) + let prefix_len = String.length prefix in + fun s -> + try + String.sub s 0 prefix_len = prefix + with Invalid_argument _ -> false + +let hashtbl_keys tbl = Hashtbl.fold (fun k _ acc -> k :: acc) tbl [] +let hashtbl_pairs tbl = Hashtbl.fold (fun k v acc -> (k,v) :: acc) tbl [] + + (** *) + exception Malformed_key of string exception Key_not_found of string exception Cyclic_definition of string @@ -36,38 +64,43 @@ exception Type_error of string * string * string (* expected type, value, msg *) exception Parse_error of string * int * int * string (* file, line, col, msg *) exception Invalid_value of (string * string) * string (* key, value, descr *) +(* type validator_id = int - (* root XML tag: used by save_to, ignored by load_from *) -let root_tag = "helm_registry" - let get_next_validator_id = let next_id = ref 0 in fun () -> incr next_id; !next_id -let magic_size = 127 let validators = Hashtbl.create magic_size -let registry = Hashtbl.create magic_size +*) + + (* root XML tag: used by save_to, ignored by load_from *) +let root_tag = "helm_registry" + +let magic_size = 127 -let backup_registry () = Hashtbl.copy registry -let restore_registry backup = +let backup_registry registry = Hashtbl.copy registry +let restore_registry backup registry = Hashtbl.clear registry; Hashtbl.iter (fun key value -> Hashtbl.replace registry key value) backup (* as \\w but: * - no sequences of '_' longer than 1 are permitted - * - no uppercase letter are permitted *) -let valid_step_rex_raw = "[a-z0-9]+\\(_[a-z0-9]+\\)*" +let valid_step_rex_raw = "[a-zA-Z0-9]+\\(_[a-z0A-Z-9]+\\)*" let valid_key_rex_raw = - sprintf "%s\(\\.%s\)*" valid_step_rex_raw valid_step_rex_raw + sprintf "%s\\(\\.%s\\)*" valid_step_rex_raw valid_step_rex_raw let valid_key_rex = Str.regexp ("^" ^ valid_key_rex_raw ^ "$") let interpolated_key_rex = Str.regexp ("\\$(" ^ valid_key_rex_raw ^ ")") let dot_rex = Str.regexp "\\." let spaces_rex = Str.regexp "[ \t\n\r]+" let heading_spaces_rex = Str.regexp "^[ \t\n\r]+" +let margin_blanks_rex = + Str.regexp "^\\([ \t\n\r]*\\)\\([^ \t\n\r]*\\)\\([ \t\n\r]*\\)$" + +let strip_blanks s = Str.global_replace margin_blanks_rex "\\2" s let split s = (* trailing blanks are removed per default by split *) @@ -81,30 +114,29 @@ let (escape, unescape) = Netencoding.Html.decode ~in_enc ~out_enc ~entity_base:`Xml ()) let key_is_valid key = -(* if not (Pcre.pmatch ~rex:valid_key_rex key) then *) if not (Str.string_match valid_key_rex key 0) then raise (Malformed_key key) +(* let value_is_valid ~key ~value = List.iter (fun (validator, descr) -> if not (validator value) then raise (Invalid_value ((key, value), descr))) (Hashtbl.find_all validators key) +*) let set' registry ~key ~value = debug_print (sprintf "Setting %s = %s" key value); key_is_valid key; - value_is_valid ~key ~value; +(* value_is_valid ~key ~value; *) Hashtbl.replace registry key value -let unset = Hashtbl.remove registry +let unset registry = Hashtbl.remove registry -let env_var_of_key key = -(* Pcre.replace ~rex:dot_rex ~templ:"__" (String.uppercase key) *) - Str.global_replace dot_rex "__" (String.uppercase key) +let env_var_of_key = Str.global_replace dot_rex "__" -let get key = +let get registry key = let rec aux stack key = key_is_valid key; if List.mem key stack then begin @@ -138,19 +170,25 @@ let get key = aux stack key) value in - aux [] key + strip_blanks (aux [] key) -let set = set' registry +let set registry = set' registry -let mk_get_set type_name (from_string: string -> 'a) (to_string: 'a -> string) = - let getter key = - let value = get key in +let has registry key = Hashtbl.mem registry key + +let mk_get_set type_name + (from_string: string -> 'a) (to_string: 'a -> string) + = + let getter registry key = + let value = get registry key in try from_string value with exn -> raise (Type_error (type_name, value, Printexc.to_string exn)) in - let setter ~key ~value = set ~key ~value:(to_string value) in + let setter registry ~key ~value = + set registry ~key ~value:(to_string value) + in (getter, setter) let (get_string, set_string) = (get, set) @@ -159,135 +197,257 @@ let (get_float, set_float) = mk_get_set "float" float_of_string string_of_float let (get_bool, set_bool) = mk_get_set "bool" bool_of_string string_of_bool let (get_string_list, set_string_list) = mk_get_set "string list" split merge -let get_opt getter key = +let get_opt registry getter key = try - Some (getter key) + Some (getter registry key) with Key_not_found _ -> None -let set_opt setter ~key ~value = +let set_opt registry setter ~key ~value = match value with - | None -> unset key - | Some value -> setter ~key ~value -let get_opt_default getter default key = - match get_opt getter key with + | None -> unset registry key + | Some value -> setter registry ~key ~value +let get_opt_default registry getter default key = + match get_opt registry getter key with | None -> default | Some v -> v +(* let add_validator ~key ~validator ~descr = let id = get_next_validator_id () in Hashtbl.add validators key (validator, descr); id +*) -open Pxp_dtd -open Pxp_document -open Pxp_types -open Pxp_yacc - -let save_to = - let dtd = new dtd PxpHelmConf.pxp_config.warner `Enc_utf8 in - let dot_RE = Str.regexp "\\." in - let create_key_node key value = (* create a value *) - let element = - create_element_node ~valcheck:false PxpHelmConf.pxp_spec dtd "key" ["name", key] - in - let data = create_data_node PxpHelmConf.pxp_spec dtd value in - element#append_node data; - element +type xml_tree = + | Cdata of string + | Element of string * (string * string) list * xml_tree list + +let dot_RE = Str.regexp "\\." + +let xml_tree_of_registry registry = + let has_child name elements = + List.exists + (function + | Element (_, ["name", name'], _) when name = name' -> true + | _ -> false) + elements in - let is_section name = - fun node -> - match node#node_type with - | T_element "section" -> - (try node#attribute "name" = Value name with Not_found -> false) - | _ -> false + let rec get_child name = function + | [] -> assert false + | (Element (_, ["name", name'], _) as child) :: tl when name = name' -> + child, tl + | hd :: tl -> + let child, rest = get_child name tl in + child, hd :: rest in - let add_key_node root sections key value = - let rec aux node = function - | [] -> - let key_node = create_key_node key value in - node#append_node key_node - | section :: tl -> - let next_node = - try - find ~deeply:false (is_section section) node - with Not_found -> - let section_node = - create_element_node ~valcheck:false PxpHelmConf.pxp_spec dtd - "section" ["name", section] - in - node#append_node section_node; - section_node - in - aux next_node tl - in - aux root sections + let rec add_key path value tree = + match path, tree with + | [key], Element (name, attrs, children) -> + Element (name, attrs, + Element ("key", ["name", key], + [Cdata (strip_blanks value)]) :: children) + | dir :: path, Element (name, attrs, children) -> + if has_child dir children then + let child, rest = get_child dir children in + Element (name, attrs, add_key path value child :: rest) + else + Element (name, attrs, + ((add_key path value (Element ("section", ["name", dir], []))) + :: children)) + | _ -> assert false in - fun fname -> - let xml_root = - create_element_node ~valcheck:false PxpHelmConf.pxp_spec dtd "helm_registry" [] - in - Hashtbl.iter - (fun key value -> - let sections, key = - let hd, tl = - match List.rev (Str.split dot_RE key) with - | hd :: tl -> hd, tl - | _ -> assert false - in - List.rev tl, hd - in - add_key_node xml_root sections key value) - registry; - let outchan = (* let's write xml output to fname *) - if Unix.system "xmllint --version &> /dev/null" = Unix.WEXITED 0 then - (* xmllint available, use it! *) - Unix.open_process_out (sprintf - "xmllint --format --encode utf8 -o '%s' -" fname) + Hashtbl.fold + (fun k v tree -> add_key ("helm_registry" :: (Str.split dot_RE k)) v tree) + registry + (Element ("helm_registry", [], [])) + +let rec stream_of_xml_tree = function + | Cdata s -> Xml.xml_cdata s + | Element (name, attrs, children) -> + Xml.xml_nempty name + (List.map (fun (n, v) -> (None, n, v)) attrs) + (stream_of_xml_trees children) +and stream_of_xml_trees = function + | [] -> [< >] + | hd :: tl -> [< stream_of_xml_tree hd; stream_of_xml_trees tl >] + +let save_to registry fname = + let token_stream = stream_of_xml_tree (xml_tree_of_registry registry) in + let oc = open_out fname in + Xml.pp_to_outchan token_stream oc; + close_out oc + +(* PXP version *) +(*open Pxp_dtd*) +(*open Pxp_document*) +(*open Pxp_types*) +(*open Pxp_yacc*) + +(*let save_to =*) +(* let dtd = new dtd PxpHelmConf.pxp_config.warner `Enc_utf8 in*) +(* let create_key_node key value = |+ create a value +|*) +(* let element =*) +(* create_element_node ~valcheck:false PxpHelmConf.pxp_spec dtd*) +(* "key" ["name", key]*) +(* in*) +(* let data = create_data_node PxpHelmConf.pxp_spec dtd value in*) +(* element#append_node data;*) +(* element*) +(* in*) +(* let is_section name =*) +(* fun node ->*) +(* match node#node_type with*) +(* | T_element "section" ->*) +(* (try node#attribute "name" = Value name with Not_found -> false)*) +(* | _ -> false*) +(* in*) +(* let add_key_node root sections key value =*) +(* let rec aux node = function*) +(* | [] ->*) +(* let key_node = create_key_node key value in*) +(* node#append_node key_node*) +(* | section :: tl ->*) +(* let next_node =*) +(* try*) +(* find ~deeply:false (is_section section) node*) +(* with Not_found ->*) +(* let section_node =*) +(* create_element_node ~valcheck:false PxpHelmConf.pxp_spec dtd*) +(* "section" ["name", section]*) +(* in*) +(* node#append_node section_node;*) +(* section_node*) +(* in*) +(* aux next_node tl*) +(* in*) +(* aux root sections*) +(* in*) +(* fun registry fname ->*) +(* let xml_root =*) +(* create_element_node ~valcheck:false PxpHelmConf.pxp_spec dtd*) +(* "helm_registry" []*) +(* in*) +(* Hashtbl.iter*) +(* (fun key value ->*) +(* let sections, key =*) +(* let hd, tl =*) +(* match List.rev (Str.split dot_RE key) with*) +(* | hd :: tl -> hd, tl*) +(* | _ -> assert false*) +(* in*) +(* List.rev tl, hd*) +(* in*) +(* add_key_node xml_root sections key value)*) +(* registry;*) +(* let outfile = open_out fname in*) +(* Unix.lockf (Unix.descr_of_out_channel outfile) Unix.F_LOCK 0; |+ blocks +|*) +(* if*) +(* Unix.system "xmllint --version &> /dev/null" = Unix.WEXITED 0*) +(* then begin*) +(* let (xmllint_in, xmllint_out) =*) +(* Unix.open_process "xmllint --format --encode utf8 -"*) +(* in*) +(* xml_root#write (`Out_channel xmllint_out) `Enc_utf8;*) +(* close_out xmllint_out;*) +(* try*) +(* while true do*) +(* output_string outfile (input_line xmllint_in ^ "\n")*) +(* done*) +(* with End_of_file ->*) +(* close_in xmllint_in;*) +(* ignore (Unix.close_process (xmllint_in, xmllint_out))*) +(* end else*) +(* xml_root#write (`Out_channel outfile) `Enc_utf8;*) +(* Unix.lockf (Unix.descr_of_out_channel outfile) Unix.F_ULOCK 0;*) +(* close_out outfile*) + +(* PXP version *) +(*let load_from_absolute =*) +(* let config = PxpHelmConf.pxp_config in*) +(* let entry = `Entry_document [ `Extend_dtd_fully; `Parse_xml_decl ] in*) +(* let fold_key key_stack key =*) +(* match key_stack with*) +(* | [] -> key*) +(* | _ -> String.concat "." key_stack ^ "." ^ key*) +(* in*) +(* fun registry fname ->*) +(* debug_print ("Loading configuration from " ^ fname);*) +(* let document =*) +(* parse_wfdocument_entity config (from_file fname) PxpHelmConf.pxp_spec*) +(* in*) +(* let rec aux key_stack node =*) +(* node#iter_nodes (fun n ->*) +(* try*) +(* (match n#node_type with*) +(* | T_element "section" ->*) +(* let section = n#required_string_attribute "name" in*) +(* aux (key_stack @ [section]) n*) +(* | T_element "key" ->*) +(* let key = n#required_string_attribute "name" in*) +(* let value = n#data in*) +(* set registry ~key:(fold_key key_stack key) ~value*) +(* | _ -> ())*) +(* with exn ->*) +(* let (fname, line, pos) = n#position in*) +(* raise (Parse_error (fname, line, pos,*) +(* "Uncaught exception: " ^ Printexc.to_string exn)))*) +(* in*) +(* let backup = backup_registry registry in*) +(* Hashtbl.clear registry;*) +(* try*) +(* aux [] document#root*) +(* with exn ->*) +(* restore_registry backup registry;*) +(* raise exn*) + +(* XmlPushParser version *) +let load_from_absolute registry fname = + let path = ref [] in (*
elements entered so far *) + let in_key = ref false in (* have we entered a element? *) + let push_path name = path := name :: !path in + let pop_path () = path := List.tl !path in + let start_element tag attrs = + match tag, attrs with + | "section", ["name", name] -> push_path name + | "key", ["name", name] -> in_key := true; push_path name + | "helm_registry", _ -> () + | tag, _ -> + raise (Parse_error (fname, ~-1, ~-1, + (sprintf "unexpected element <%s> or wrong attribute set" tag))) + in + let end_element tag = + match tag with + | "section" -> pop_path () + | "key" -> in_key := false; pop_path () + | "helm_registry" -> () + | _ -> assert false + in + let character_data text = + if !in_key then + let key = String.concat "." (List.rev !path) in + let value = + if Hashtbl.mem registry key then + Hashtbl.find registry key ^ text else - (* xmllint not available, write pxp ugly output directly to fname *) - open_out fname + text in - xml_root#write (`Out_channel outchan) `Enc_utf8; - close_out outchan - -let load_from_absolute = - let config = PxpHelmConf.pxp_config in - let entry = `Entry_document [ `Extend_dtd_fully; `Parse_xml_decl ] in - let fold_key key_stack key = - match key_stack with - | [] -> key - | _ -> String.concat "." key_stack ^ "." ^ key + set registry ~key ~value in - fun fname -> - debug_print ("Loading configuration from " ^ fname); - let document = - parse_wfdocument_entity config (from_file fname) PxpHelmConf.pxp_spec - in - let rec aux key_stack node = - node#iter_nodes (fun n -> - try - (match n#node_type with - | T_element "section" -> - let section = n#required_string_attribute "name" in - aux (key_stack @ [section]) n - | T_element "key" -> - let key = n#required_string_attribute "name" in - let value = n#data in - set ~key:(fold_key key_stack key) ~value - | _ -> ()) - with exn -> - let (fname, line, pos) = n#position in - raise (Parse_error (fname, line, pos, - "Uncaught exception: " ^ Printexc.to_string exn))) - in - let backup = backup_registry () in - Hashtbl.clear registry; - try - aux [] document#root - with exn -> - restore_registry backup; - raise exn + let callbacks = { + XmlPushParser.default_callbacks with + XmlPushParser.start_element = Some start_element; + XmlPushParser.end_element = Some end_element; + XmlPushParser.character_data = Some character_data; + } in + let xml_parser = XmlPushParser.create_parser callbacks in + let backup = backup_registry registry in + Hashtbl.clear registry; + try + XmlPushParser.parse xml_parser (`File fname) + with exn -> + restore_registry backup registry; + raise exn -let load_from ?path fname = +let load_from registry ?path fname = if Filename.is_relative fname then begin let no_file_found = ref true in let path = @@ -300,7 +460,7 @@ let load_from ?path fname = let conffile = dir ^ "/" ^ fname in if Sys.file_exists conffile then begin no_file_found := false; - load_from_absolute conffile + load_from_absolute registry conffile end) path; if !no_file_found then @@ -308,9 +468,136 @@ let load_from ?path fname = "Helm_registry.init: no configuration file named %s in [ %s ]" fname (String.concat "; " path)) end else - load_from_absolute fname + load_from_absolute registry fname + +let fold registry ?prefix ?(interpolate = true) f init = + let value_of k v = if interpolate then get registry k else strip_blanks v in + match prefix with + | None -> Hashtbl.fold (fun k v acc -> f acc k (value_of k v)) registry init + | Some s -> + let key_matches = starts_with (s ^ ".") in + let rec fold_filter acc = function + | [] -> acc + | (k,v) :: tl when key_matches k -> + fold_filter (f acc k (value_of k v)) tl + | _ :: tl -> fold_filter acc tl + in + fold_filter init (hashtbl_pairs registry) - (* DEBUGGING ONLY *) +let iter registry ?prefix ?interpolate f = + fold registry ?prefix ?interpolate (fun _ k v -> f k v) () +let to_list registry ?prefix ?interpolate () = + fold registry ?prefix ?interpolate (fun acc k v -> (k, v) :: acc) [] -let dump () = Hashtbl.iter (fun k v -> printf "%s = %s\n" k v) registry +let ls registry prefix = + let prefix = prefix ^ "." in + let prefix_len = String.length prefix in + let key_matches = starts_with prefix in + let matching_keys = (* collect matching keys' _postfixes_ *) + fold registry + (fun acc key _ -> + if key_matches key then + String.sub key prefix_len (String.length key - prefix_len) :: acc + else + acc) + [] + in + let (sections, keys) = + List.fold_left + (fun (sections, keys) postfix -> + match Str.split dot_rex postfix with + | [key] -> (sections, key :: keys) + | hd_key :: _ -> (* length > 1 => nested section found *) + (hd_key :: sections, keys) + | _ -> assert false) + ([], []) matching_keys + in + (list_uniq (List.sort Pervasives.compare sections), keys) + +(** {2 OO interface} *) + +class registry ?path fname = + object (self) + val _registry = Hashtbl.create magic_size + initializer load_from _registry ?path fname + method get = get _registry + method set = set _registry + method has = has _registry + method unset = unset _registry + method fold: + 'a. + ?prefix:string -> ?interpolate: bool -> + ('a -> string -> string -> 'a) -> 'a -> 'a + = + fun ?prefix ?interpolate f init -> + fold _registry ?prefix ?interpolate f init + method iter = iter _registry + method to_list = to_list _registry + method ls = ls _registry + method get_string = get_string _registry + method get_int = get_int _registry + method get_float = get_float _registry + method get_bool = get_bool _registry + method get_string_list = get_string_list _registry + method set_string = set_string _registry + method set_int = set_int _registry + method set_float = set_float _registry + method set_bool = set_bool _registry + method set_string_list = set_string_list _registry + method get_opt: 'a. (string -> 'a) -> string -> 'a option = + fun getter key -> + try Some (getter key) with Key_not_found _ -> None + method set_opt: + 'a. (key:string -> value:'a -> unit) -> key:string -> value:'a option -> + unit + = + fun setter ~key ~value -> + match value with + | None -> self#unset key + | Some value -> setter ~key ~value + method get_opt_default: 'a. (string -> 'a) -> 'a -> string -> 'a = + fun getter default key -> + match self#get_opt getter key with + | None -> default + | Some v -> v + method save_to = save_to _registry +(* method load_from = load_from _registry *) + end + +(** {2 API implementation} + * functional methods above are wrapped so that they work on a default + * (imperative) registry*) + +let default_registry = Hashtbl.create magic_size + +let get = get default_registry +let set = set default_registry +let has = has default_registry +let fold ?prefix ?interpolate f init = + fold default_registry ?prefix ?interpolate f init +let iter = iter default_registry +let to_list = to_list default_registry +let ls = ls default_registry +let get_string = get_string default_registry +let get_int = get_int default_registry +let get_float = get_float default_registry +let get_bool = get_bool default_registry +let get_string_list = get_string_list default_registry +let set_string = set_string default_registry +let set_int = set_int default_registry +let set_float = set_float default_registry +let set_bool = set_bool default_registry +let set_string_list = set_string_list default_registry +let get_opt getter key = try Some (getter key) with Key_not_found _ -> None +let set_opt setter ~key ~value = + match value with + | None -> unset default_registry key + | Some value -> setter ~key ~value +let unset = unset default_registry +let get_opt_default getter default key = + match get_opt getter key with + | None -> default + | Some v -> v +let save_to = save_to default_registry +let load_from = load_from default_registry