1 (* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
3 This file is part of mldonkey.
5 mldonkey is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 mldonkey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with mldonkey; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 This will enable very simple configuration, by a mouse-based configurator.
23 Options will be defined by a special function, which will also check
24 if a value has been provided by the user in its .gwmlrc file.
25 The .gwmlrc will be created by a dedicated tool, which could be used
26 to generate both .gwmlrc and .efunsrc files.
33 Module of option_module
34 | StringValue of string
37 | List of option_value list
38 | SmallList of option_value list
39 | OnceValue of option_value
40 | DelayedValue of (out_channel -> string -> unit)
41 and option_module = (string * option_value) list
43 exception SideEffectOption
44 exception OptionNotFound
46 type 'a option_class =
47 { class_name : string;
48 from_value : option_value -> 'a;
49 to_value : 'a -> option_value;
50 mutable class_hooks : ('a option_record -> unit) list;
51 mutable string_wrappers : (('a -> string) * (string -> 'a)) option;
53 and 'a option_record =
55 option_class : 'a option_class;
56 mutable option_value : 'a;
57 option_name : string list;
58 mutable option_desc : string;
61 mutable option_hooks : (unit -> unit) list;
62 option_section : options_section;
63 option_advanced : bool;
64 option_restart : bool;
66 option_internal : bool;
69 { mutable file_name : string;
70 mutable file_sections : options_section list;
71 mutable file_rc : option_module;
72 mutable file_pruned : bool;
74 mutable file_before_save_hook : (unit -> unit);
75 mutable file_after_save_hook : (unit -> unit);
76 mutable file_after_load_hook : (unit -> unit);
78 and options_section = {
79 section_name : string list;
80 section_help : string;
81 section_file : options_file;
82 mutable section_options : Obj.t option_record list;
85 let file_section file section_name section_help =
87 section_name = section_name;
88 section_help = section_help;
93 file.file_sections <- file.file_sections @ [s];
96 let create_options_file name =
104 file_before_save_hook = (fun _ -> ());
105 file_after_save_hook = (fun _ -> ());
106 file_after_load_hook = (fun _ -> ());
109 ignore (file_section file ["Header"] "These options must be read first");
112 let set_options_file opfile name = opfile.file_name <- name
113 let print_options_not_found = ref false
115 let define_option_class
116 (class_name : string) (from_value : option_value -> 'a)
117 (to_value : 'a -> option_value) =
119 {class_name = class_name; from_value = from_value; to_value = to_value;
120 class_hooks = []; string_wrappers = None;}
125 let rec find_value list m =
127 [] -> raise Not_found
129 let m = List.assoc name m in
132 | Module m, _ :: _ -> find_value tail m
133 | _ -> raise Not_found
135 let find_value list m =
136 try find_value list m with
137 _ -> raise OptionNotFound
139 let prune_file file = file.file_pruned <- true
141 let define_simple_option
142 normalp (section : options_section) (option_name : string list) desc
143 restart public internal
144 (option_help : string) (option_class : 'a option_class)
145 (default_value : 'a) (advanced : bool) =
146 let desc = match desc with None -> "" | Some s -> s in
148 { option_name = option_name; option_help = option_help;
149 option_class = option_class; option_value = default_value;
150 option_default = default_value;
151 option_hooks = []; option_section = section;
152 option_restart = (match restart with None -> false | Some v -> v);
153 option_public = (match public with None -> false | Some v -> v);
154 option_internal = (match internal with None -> false | Some v -> v);
155 option_advanced = advanced; option_desc = desc; }
157 section.section_options <-
158 section.section_options @ [ (Obj.magic o : Obj.t option_record) ];
161 o.option_class.from_value (
162 find_value option_name section.section_file.file_rc)
164 OptionNotFound -> default_value
166 lprintf "Options.define_option, for option %s: "
167 (match option_name with
169 | name :: _ -> name);
170 lprintf "%s\n" (Printexc2.to_string e);
175 let define_header_option
176 opfile option_name option_help option_class default_value =
177 define_simple_option false (List.hd opfile.file_sections)
178 option_name None None None None option_help option_class
181 let define_option opfile option_name ?desc ?restart ?public ?internal option_help option_class default_value =
182 define_simple_option true opfile option_name desc restart public internal option_help option_class
185 let define_expert_option
186 opfile option_name ?desc ?restart ?public ?internal option_help option_class default_value =
187 define_simple_option true opfile option_name desc restart public internal option_help option_class
193 let once_values = Hashtbl.create 13
194 let once_values_counter = ref 0
195 let once_values_rev = Hashtbl.create 13
197 let lexer = make_lexer ["="; "{"; "}"; "["; "]"; ";"; "("; ")"; ","; "."; "@"]
199 let rec parse_gwmlrc = parser
200 | [< id = parse_id; 'Kwd "="; v = parse_option ;
201 eof = parse_gwmlrc >] -> (id, v) :: eof
204 and parse_option = parser
205 | [< 'Kwd "{"; v = parse_gwmlrc; 'Kwd "}" >] -> Module v
206 | [< 'Ident s >] -> StringValue s
207 | [< 'String s >] -> StringValue s
208 | [< 'Int i >] -> IntValue i
209 | [< 'Float f >] -> FloatValue f
210 | [< 'Kwd "@"; 'Int i; v = parse_once_value i >] -> OnceValue v
211 | [< 'Char c >] -> StringValue (let s = String.create 1 in s.[0] <- c; s)
212 | [< 'Kwd "["; v = parse_list [] >] -> List v
213 | [< 'Kwd "("; v = parse_list [] >] -> List v
215 and parse_once_value i = parser
218 try Hashtbl.find once_values i with Not_found ->
219 lprintf "Error in saved file: @%Ld@ not defined\n" i;
222 | [< 'Kwd "="; v = parse_option >] ->
224 Hashtbl.add once_values i v;
228 and parse_id = parser
230 | [< 'String s >] -> s
232 and parse_list list = parser
233 [< 'Kwd ";"; strm >] -> parse_list (list) strm
234 | [< 'Kwd ","; strm >] -> parse_list (list) strm
235 | [< 'Kwd "."; strm >] -> parse_list (list) strm
236 | [< v = parse_option; strm >] -> parse_list (v :: list) strm
237 | [< 'Kwd "]" >] -> List.rev list
238 | [< 'Kwd ")" >] -> List.rev list
253 o.option_class.class_hooks
255 let really_load filename sections =
256 let temp_file = filename ^ ".tmp" in
257 if Sys.file_exists temp_file then
259 lprintf "File %s exists\n" temp_file;
260 lprintf "An error may have occurred during previous configuration save.\n";
261 lprintf "Please, check your configurations files, and rename/remove this file\n";
262 lprintf "before restarting\n";
265 Unix2.tryopen_read filename (fun ic ->
266 let s = Stream.of_channel ic in
268 let stream = lexer s in
269 Hashtbl.clear once_values;
274 lprintf "Syntax error while parsing file %s at pos %d:(%s)\n"
275 filename (Stream.count s) (Printexc2.to_string e);
276 lprintf "it seems that %s is corrupt,\n" filename;
277 lprintf "try to use a backup from %s\n"
278 (Filename.concat (Sys.getcwd ()) "old_config");
280 Hashtbl.clear once_values;
281 let affect_option o =
285 o.option_class.from_value (find_value o.option_name list)
286 with SideEffectOption -> ());
290 | SideEffectOption -> ()
292 if !print_options_not_found then
295 List.iter (fun s -> lprintf "%s " s) o.option_name;
296 lprintf "not found in %s\n" filename;
299 lprintf "Exception: %s while handling option:"
300 (Printexc2.to_string e);
301 List.iter (fun s -> lprintf "%s " s) o.option_name;
303 lprintf " in %s\n" filename;
304 lprintf "Aborting\n.";
308 (* The options are affected by sections, from the first defined one to
309 the last defined one ("defined" in the order of the program execution).
310 Don't change this. *)
312 List.iter affect_option s.section_options) sections;
315 lprintf "Error %s in %s\n" (Printexc2.to_string e) filename;
322 let unsafe_get = String.unsafe_get
323 external is_printable : char -> bool = "caml_is_printable"
324 let unsafe_set = String.unsafe_set
328 for i = 0 to String.length s - 1 do
331 (match unsafe_get s i with
334 | c -> if is_printable c then 1 else 4)
336 if !n = String.length s then s
338 let s' = String.create !n in
340 for i = 0 to String.length s - 1 do
341 begin match unsafe_get s i with
342 '"' | '\\' as c -> unsafe_set s' !n '\\'; incr n; unsafe_set s' !n c
343 | '\n' | '\t' as c -> unsafe_set s' !n c
345 if is_printable c then unsafe_set s' !n c
347 let a = int_of_char c in
348 unsafe_set s' !n '\\';
350 unsafe_set s' !n (char_of_int (48 + a / 100));
352 unsafe_set s' !n (char_of_int (48 + a / 10 mod 10));
354 unsafe_set s' !n (char_of_int (48 + a mod 10))
361 if s = "" then "\"\""
365 'a'..'z' | 'A'..'Z' ->
366 for i = 1 to String.length s - 1 do
368 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' -> ()
369 | _ -> raise exit_exn
373 if Int64.to_string (Int64.of_string s) = s ||
374 string_of_float (float_of_string s) = s
379 _ -> Printf.sprintf "\"%s\"" (escaped s)
381 let with_help = ref false
382 let save_private = ref false
384 let tabulate s = String2.replace s '\n' "\n\t"
386 let rec save_module indent oc list =
389 (fun (name, help, restart, internal, value) ->
393 if !with_help && help <> "" then
394 Printf.fprintf oc "\n\t(* %s *)\n" (tabulate help);
396 Printf.fprintf oc "\t(* changing this option requires restart of MLDonkey core *)\n";
398 Printf.fprintf oc "\t(* Do not change this option, internal use only! *)\n";
399 Printf.fprintf oc "%s %s = " indent (safe_string name);
400 save_value indent oc value;
401 Printf.fprintf oc "\n"
404 try List.assoc m !subm with
405 e -> let p = ref [] in subm := (m, p) :: !subm; p
407 p := (tail, help, restart, internal, value) :: !p)
411 Printf.fprintf oc "%s %s = {\n" indent (safe_string m);
412 save_module (indent ^ " ") oc !p;
413 Printf.fprintf oc "%s}\n" indent)
415 and save_list indent oc list =
418 | [v] -> save_value indent oc v
420 save_value indent oc v; Printf.fprintf oc ", "; save_list indent oc tail
421 and save_list_nl indent oc list =
424 | [v] -> Printf.fprintf oc "\n%s" indent; save_value indent oc v
426 Printf.fprintf oc "\n%s" indent;
427 save_value indent oc v;
428 Printf.fprintf oc ";";
429 save_list_nl indent oc tail
430 and save_value indent oc v =
432 StringValue s -> Printf.fprintf oc "%s" (safe_string s)
433 | IntValue i -> Printf.fprintf oc "%s" (Int64.to_string i)
434 | FloatValue f -> Printf.fprintf oc "%F" f
436 Printf.fprintf oc "[";
437 save_list_nl (indent ^ " ") oc l;
438 Printf.fprintf oc "]"
439 | DelayedValue f -> f oc indent
441 Printf.fprintf oc "(";
442 save_list (indent ^ " ") oc l;
443 Printf.fprintf oc ")"
445 Printf.fprintf oc "{";
446 save_module_fields (indent ^ " ") oc m;
447 Printf.fprintf oc "}"
450 let i = Hashtbl.find once_values_rev v in Printf.fprintf oc "@%Ld@" i
453 incr once_values_counter;
454 let i = Int64.of_int !once_values_counter in
455 Hashtbl.add once_values_rev v i;
456 Printf.fprintf oc "@%Ld = " i;
457 save_value indent oc v
458 and save_module_fields indent oc m =
461 | (name, v) :: tail ->
462 Printf.fprintf oc "%s %s = " indent (safe_string name);
463 save_value indent oc v;
464 Printf.fprintf oc "\n";
465 save_module_fields indent oc tail
467 let options_file_name f = f.file_name
472 really_load opfile.file_name opfile.file_sections;
474 | Not_found | Sys_error _ -> ());
475 opfile.file_after_load_hook ()
477 let append opfile filename =
480 really_load filename opfile.file_sections @
483 Not_found -> lprintf "No %s found\n" filename
485 let ( !! ) o = o.option_value
486 let ( =:= ) o v = o.option_value <- v; exec_chooks o; exec_hooks o
488 let value_to_stringoption v =
490 StringValue s -> if s = "None" then None else Some s
491 | _ -> failwith "Not a string option"
493 let stringoption_to_value v =
495 None -> StringValue "None"
496 | Some s -> StringValue s
498 let rec value_to_string v =
501 | IntValue i -> Int64.to_string i
502 | FloatValue f -> string_of_float f
503 | OnceValue v -> value_to_string v
504 | _ -> failwith "Not a string option"
506 let safe_value_to_string v =
509 | IntValue i -> Int64.to_string i
510 | FloatValue f -> string_of_float f
511 | OnceValue v -> value_to_string v
514 let string_to_value s = StringValue s
516 let rec value_to_int64 v =
518 StringValue s -> Int64.of_string s
520 | FloatValue i -> Int64.of_float i
521 | OnceValue v -> value_to_int64 v
522 | _ -> failwith "Options: not an int option"
524 let value_to_int v = Int64.to_int (value_to_int64 v)
525 let int_to_value i = IntValue (Int64.of_int i)
526 let int64_to_value i = IntValue i
528 let percent_to_value i = IntValue (Int64.of_int i)
529 let value_to_percent v =
530 match Int64.to_int (value_to_int64 v) with
532 | v when v > 100 -> 100
535 let port_to_value i = IntValue (Int64.of_int i)
536 let value_to_port v =
537 match Int64.to_int (value_to_int64 v) with
538 | v when v < 0 -> 2000 + Random.int 40000
539 | v when v > 65535 -> 2000 + Random.int 40000
542 (* The Pervasives version is too restrictive *)
543 let bool_of_string s =
544 match String.lowercase s with
551 | _ -> invalid_arg "bool_of_string"
553 let rec value_to_bool v =
555 StringValue s -> bool_of_string s
556 | IntValue v when v = Int64.zero -> false
557 | IntValue v when v = Int64.one -> true
558 | OnceValue v -> value_to_bool v
559 | _ -> failwith "Options: not a bool option"
560 let bool_to_value i = StringValue (string_of_bool i)
562 let rec value_to_float v =
564 StringValue s -> float_of_string s
566 | OnceValue v -> value_to_float v
567 | _ -> failwith "Options: not a float option"
569 let float_to_value i = FloatValue i
571 let rec value_to_string2 v =
573 List [s1; s2] | SmallList [s1; s2] ->
574 value_to_string s1, value_to_string s2
575 | OnceValue v -> value_to_string2 v
576 | _ -> failwith "Options: not a string2 option"
578 let string2_to_value (s1, s2) = SmallList [StringValue s1; StringValue s2]
580 let rec value_to_list v2c v =
582 List l | SmallList l -> List.rev (List.rev_map v2c l)
583 | OnceValue v -> value_to_list v2c v
586 (Printf.sprintf "Options: not a list option (StringValue [%s])" s)
587 | FloatValue _ -> failwith "Options: not a list option (FloatValue)"
588 | IntValue _ -> failwith "Options: not a list option (IntValue)"
589 | Module _ -> failwith "Options: not a list option (Module)"
590 | DelayedValue _ -> failwith "Options: not a list option (Delayed)"
592 let rec value_to_hasharray v2c v =
595 let hash = Array.init 256 (fun _ -> Hashtbl.create 10) in
598 let (num, md4, peer) = v2c a in Hashtbl.add hash.(num) md4 peer)
601 | OnceValue v -> value_to_hasharray v2c v
602 | _ -> failwith (Printf.sprintf "Options: not a list option for list2")
604 let rec value_to_safelist v2c v =
606 List l | SmallList l ->
607 let rec iter list left =
612 try v2c x :: list with
617 List.rev (iter [] (List.rev l))
618 | OnceValue v -> value_to_safelist v2c v
621 (Printf.sprintf "Options: not a list option (StringValue [%s])" s)
622 | FloatValue _ -> failwith "Options: not a list option (FloatValue)"
623 | IntValue _ -> failwith "Options: not a list option (IntValue)"
624 | Module _ -> failwith "Options: not a list option (Module)"
625 | DelayedValue _ -> failwith "Options: not a list option (Delayed)"
627 let rec value_to_intmap f v2c v =
629 List l | SmallList l ->
630 let rec iter map left =
635 try let v = v2c x in let num = f v in Intmap.add num v map with
641 | OnceValue v -> value_to_intmap f v2c v
644 (Printf.sprintf "Options: not a list option (StringValue [%s])" s)
645 | FloatValue _ -> failwith "Options: not a list option (FloatValue)"
646 | IntValue _ -> failwith "Options: not a list option (IntValue)"
647 | Module _ -> failwith "Options: not a list option (Module)"
648 | DelayedValue _ -> failwith "Options: not a list option (Delayed)"
650 let rec value_to_listiter v2c v =
652 List l | SmallList l ->
655 try ignore (v2c v) with
656 SideEffectOption -> ())
658 raise SideEffectOption
659 | OnceValue v -> value_to_listiter v2c v
662 (Printf.sprintf "Options: not a list option (StringValue [%s])" s)
663 | FloatValue _ -> failwith "Options: not a list option (FloatValue)"
664 | IntValue _ -> failwith "Options: not a list option (IntValue)"
665 | Module _ -> failwith "Options: not a list option (Module)"
666 | DelayedValue _ -> failwith "Options: not a list option (Delayed)"
668 let rec convert_list c2v l res =
673 try Some (c2v v) with
675 lprintf "Exception %s in Options.convert_list\n"
676 (Printexc2.to_string e);
679 None -> convert_list c2v list res
680 | Some v -> convert_list c2v list (v :: res)
682 let option_to_value c2v o =
684 None -> StringValue ""
687 let rec value_to_option v2c v =
689 StringValue "" -> None
690 | OnceValue v -> value_to_option v2c v
693 let save_delayed_list_value oc indent c2v =
694 let indent = indent ^ " " in
698 Printf.fprintf oc "\n%s" indent;
699 save_value indent oc v;
700 Printf.fprintf oc ";"
703 let list_to_value c2v l =
706 Printf.fprintf oc "[";
707 List.iter (save_delayed_list_value oc indent c2v) l;
708 Printf.fprintf oc "]")
710 let intmap_to_value name c2v map =
713 let save = save_delayed_list_value oc indent c2v in
714 Printf.fprintf oc "[";
715 Intmap.iter (fun _ v -> save v) map;
716 Printf.fprintf oc "]")
718 let hasharray_to_value x c2v l =
721 Printf.fprintf oc "[";
722 let save = save_delayed_list_value oc indent c2v in
723 for i = 0 to Array.length l - 1 do
724 Hashtbl.iter (fun a b -> save (0, x, b)) l.(i)
726 Printf.fprintf oc "]")
728 let smalllist_to_value c2v l = SmallList (convert_list c2v l [])
730 let value_to_path v =
731 List.map Filename2.from_string
734 StringValue s -> Filepath.string_to_colonpath s
735 | OnceValue v -> iter v
740 StringValue s -> Filename2.from_string s
741 | _ -> failwith "Options: not a path option")
743 | _ -> failwith "Options: not path bool option"
747 let path_to_value list =
748 StringValue (Filepath.colonpath_to_string (List.map Filename2.to_string list))
751 define_option_class "String" value_to_string string_to_value
752 let color_option = define_option_class "Color" value_to_string string_to_value
753 let font_option = define_option_class "Font" value_to_string string_to_value
755 let int_option = define_option_class "Int" value_to_int int_to_value
756 let int64_option = define_option_class "Int64" value_to_int64 int64_to_value
757 let percent_option = define_option_class "Int" value_to_percent percent_to_value
758 let port_option = define_option_class "Int" value_to_port port_to_value
760 let bool_option = define_option_class "Bool" value_to_bool bool_to_value
761 let float_option = define_option_class "Float" value_to_float float_to_value
762 let path_option = define_option_class "Path" value_to_path path_to_value
765 define_option_class "String2" value_to_string2 string2_to_value
767 let option_option cl =
768 define_option_class (cl.class_name ^ " Option")
769 (value_to_option cl.from_value) (option_to_value cl.to_value)
772 define_option_class (cl.class_name ^ " List") (value_to_list cl.from_value)
773 (list_to_value cl.to_value)
775 let value_to_array from_value a =
776 Array.of_list (value_to_list from_value a)
777 let array_to_value to_value v =
778 list_to_value to_value (Array.to_list v)
780 let array_option cl =
781 define_option_class (cl.class_name ^ " Array")
782 (fun v -> Array.of_list (value_to_list cl.from_value v))
783 (fun v -> list_to_value cl.to_value (Array.to_list v))
785 let hasharray_option x cl =
786 define_option_class "Hashtable array" (value_to_hasharray cl.from_value)
787 (hasharray_to_value x cl.to_value)
789 let safelist_option cl =
790 define_option_class (cl.class_name ^ " List")
791 (value_to_safelist cl.from_value)
792 (list_to_value cl.to_value)
794 let intmap_option f cl =
795 define_option_class (cl.class_name ^ " Intmap")
796 (value_to_intmap f cl.from_value)
797 (intmap_to_value cl.class_name cl.to_value)
799 let listiter_option cl =
800 define_option_class (cl.class_name ^ " List")
801 (value_to_listiter cl.from_value)
802 (list_to_value cl.to_value)
804 let smalllist_option cl =
805 define_option_class (cl.class_name ^ " List") (value_to_list cl.from_value)
806 (smalllist_to_value cl.to_value)
808 let to_value cl = cl.to_value
809 let from_value cl = cl.from_value
811 let rec value_to_sum l v =
813 StringValue s -> List.assoc s l
814 | OnceValue v -> value_to_sum l v
815 | _ -> failwith "Options: not a sum option"
817 let sum_to_value l v = StringValue (List.assq v l)
820 let ll = List.map (fun (a1, a2) -> a2, a1) l in
821 define_option_class "Sum" (value_to_sum l) (sum_to_value ll)
823 let option_to_value o =
824 o.option_name, o.option_help,
825 o.option_restart, o.option_internal,
826 (try o.option_class.to_value o.option_value with
828 lprintf "Error while saving option \"%s\": %s\n"
829 (try List.hd o.option_name with
831 (Printexc2.to_string e);
834 let string_of_string_list list =
835 let rec iter s list =
839 iter (Printf.sprintf "%s.%s" s ss) tail
843 | s :: tail -> iter s tail
845 let title_opfile = ref true;;
848 opfile.file_before_save_hook ();
850 let old_config_dir = "old_config" in
851 if not (Sys.file_exists old_config_dir) then Unix.mkdir old_config_dir 0o755;
853 let filename = opfile.file_name in
854 let old_file = Filename.concat old_config_dir filename in
857 Unix2.with_remove (filename ^ ".tmp") begin fun temp_file ->
858 Unix2.tryopen_write temp_file (fun oc ->
860 if !save_private then (try Unix.chmod temp_file 0o600 with _ -> ());
861 once_values_counter := 0;
862 title_opfile := true;
863 Hashtbl.clear once_values_rev;
864 let advanced = ref false in
866 let options = List.filter (fun o ->
867 if o.option_advanced then advanced := true;
868 not o.option_advanced) s.section_options in
869 if options <> [] then begin
870 if s.section_name <> [] then begin
871 Printf.fprintf oc "\n\n";
872 Printf.fprintf oc " (************************************)\n";
873 if !title_opfile then begin
874 Printf.fprintf oc " (* Never edit options files when *)\n";
875 Printf.fprintf oc " (* the daemon is running *)\n";
876 Printf.fprintf oc " (************************************)\n";
877 title_opfile := false;
879 Printf.fprintf oc " (* SECTION : %s *)\n" (string_of_string_list s.section_name);
880 Printf.fprintf oc " (* %s *)\n" s.section_help;
881 Printf.fprintf oc " (************************************)\n";
882 Printf.fprintf oc "\n\n";
884 save_module "" oc (List.map option_to_value options)
886 ) opfile.file_sections;
887 if !advanced then begin
888 Printf.fprintf oc "\n\n\n";
889 Printf.fprintf oc "(*****************************************************************)\n";
890 Printf.fprintf oc "(* *)\n";
891 Printf.fprintf oc "(* ADVANCED OPTIONS *)\n";
892 Printf.fprintf oc "(* *)\n";
893 Printf.fprintf oc "(* All the options after this line are for the expert *)\n";
894 Printf.fprintf oc "(* user. Do not modify them if you are not sure. *)\n";
895 Printf.fprintf oc "(* *)\n";
896 Printf.fprintf oc "(*****************************************************************)\n";
897 Printf.fprintf oc "\n\n\n";
899 let options = List.filter (fun o -> o.option_advanced)
901 if options = [] then () else let _ = () in
902 Printf.fprintf oc "\n\n";
903 Printf.fprintf oc " (************************************)\n";
905 Printf.fprintf oc " (* SECTION : %s FOR EXPERTS *)\n" (string_of_string_list s.section_name);
906 Printf.fprintf oc " (* %s *)\n" s.section_help;
907 Printf.fprintf oc " (************************************)\n";
908 Printf.fprintf oc "\n\n";
909 save_module "" oc (List.map option_to_value options)
910 ) opfile.file_sections;
912 if not opfile.file_pruned then
915 Printf.fprintf oc "\n(*\n The following options are not used (errors, obsolete, ...) \n*)\n";
917 (fun (name, value) ->
923 match o.option_name with
924 n :: _ -> if n = name then raise Exit
927 opfile.file_sections;
928 rem := (name, value) :: !rem;
929 Printf.fprintf oc "%s = " (safe_string name);
930 save_value " " oc value;
931 Printf.fprintf oc "\n"
935 lprintf "Exception %s in Options.save\n"
936 (Printexc2.to_string e);
939 opfile.file_rc <- !rem
942 Unix2.fsync (Unix.descr_of_out_channel oc);
943 Hashtbl.clear once_values_rev);
947 Unix2.rename filename old_file
948 with Unix.Unix_error(Unix.ENOENT, _, _) -> ();
950 Unix2.rename temp_file filename
952 lprintf_nl "[Opt] exception %s while saving %s" (Printexc2.to_string e) filename
954 end; (* remove temp_file *)
955 opfile.file_after_save_hook ();
957 opfile.file_after_save_hook ();
960 let save_with_help opfile =
962 ( try save opfile with _ -> () );
965 let save_with_help_private opfile =
967 save_private := true;
968 begin try save opfile with
972 save_private := false
974 let option_hook option f = option.option_hooks <- f :: option.option_hooks
976 let class_hook option_class f =
977 option_class.class_hooks <- f :: option_class.class_hooks
979 let rec iter_order f list =
982 | v :: tail -> f v; iter_order f tail
988 Printf.fprintf oc "OPTION \"";
989 begin match o.option_name with
990 [] -> Printf.fprintf oc "???"
991 | [name] -> Printf.fprintf oc "%s" name
993 Printf.fprintf oc "%s" name;
994 iter_order (fun name -> Printf.fprintf oc ":%s" name) o.option_name
996 Printf.fprintf oc "\" (TYPE \"%s\"): %s\n CURRENT: \n"
997 o.option_class.class_name o.option_help;
999 once_values_counter := 0;
1000 Hashtbl.clear once_values_rev;
1001 save_value "" oc (o.option_class.to_value o.option_value)
1005 Printf.fprintf oc "\n")
1007 ) opfile.file_sections;
1011 let tuple2_to_value (c1, c2) (a1, a2) =
1012 SmallList [to_value c1 a1; to_value c2 a2]
1014 let rec value_to_tuple2 (c1, c2 as cs) v =
1016 List [v1; v2] -> from_value c1 v1, from_value c2 v2
1017 | SmallList [v1; v2] -> from_value c1 v1, from_value c2 v2
1018 | OnceValue v -> value_to_tuple2 cs v
1019 | List l | SmallList l ->
1020 lprintf "list of %d\n" (List.length l);
1021 failwith "Options: not a tuple2 list option"
1022 | _ -> failwith "Options: not a tuple2 option"
1024 let tuple2_option p =
1025 define_option_class "tuple2_option" (value_to_tuple2 p) (tuple2_to_value p)
1027 let tuple3_to_value (c1, c2, c3) (a1, a2, a3) =
1028 SmallList [to_value c1 a1; to_value c2 a2; to_value c3 a3]
1029 let rec value_to_tuple3 (c1, c2, c3 as cs) v =
1031 List [v1; v2; v3] -> from_value c1 v1, from_value c2 v2, from_value c3 v3
1032 | SmallList [v1; v2; v3] ->
1033 from_value c1 v1, from_value c2 v2, from_value c3 v3
1034 | OnceValue v -> value_to_tuple3 cs v
1035 | _ -> failwith "Options: not a tuple3 option"
1037 let tuple3_option p =
1038 define_option_class "tuple3_option" (value_to_tuple3 p) (tuple3_to_value p)
1040 let tuple4_to_value (c1, c2, c3, c4) (a1, a2, a3, a4) =
1041 SmallList [to_value c1 a1; to_value c2 a2; to_value c3 a3; to_value c4 a4]
1042 let rec value_to_tuple4 (c1, c2, c3, c4 as cs) v =
1044 List [v1; v2; v3; v4] | SmallList [v1; v2; v3; v4] ->
1045 from_value c1 v1, from_value c2 v2, from_value c3 v3, from_value c4 v4
1046 | OnceValue v -> value_to_tuple4 cs v
1047 | _ -> failwith "Options: not a tuple4 option"
1049 let tuple4_option p =
1050 define_option_class "tuple4_option" (value_to_tuple4 p) (tuple4_to_value p)
1053 let value_to_filename v =
1054 Filename2.from_string
1057 | _ -> failwith "Options: not a filename option")
1059 let filename_to_value v = StringValue (Filename2.to_string v)
1061 let filename_option =
1062 define_option_class "Filename" value_to_filename filename_to_value
1064 let shortname o = String.concat ":" o.option_name
1065 let get_class o = o.option_class
1067 let help = o.option_help in if help = "" then "No Help Available" else help
1068 let advanced o = o.option_advanced
1070 let get_option opfile name =
1071 (* lprintf "get_option [%s]\n" name;*)
1072 let rec iter name list sections =
1074 | o :: list -> if o.option_name = name then o else
1075 iter name list sections
1080 (Printf.sprintf "option [%s] not_found in %s"
1081 (String.concat ";" name) opfile.file_name);
1084 iter name s.section_options tail
1086 iter [name] [] opfile.file_sections
1089 let set_simple_option opfile name v =
1090 let o = get_option opfile name in
1091 begin match o.option_class.string_wrappers with
1092 None -> o.option_value <- o.option_class.from_value (string_to_value v)
1093 | Some (_, from_string) -> o.option_value <- from_string v
1098 let get_simple_option opfile name =
1099 let o = get_option opfile name in
1100 match o.option_class.string_wrappers with
1101 None -> safe_value_to_string (o.option_class.to_value o.option_value)
1102 | Some (to_string, _) -> to_string o.option_value
1104 let set_string_wrappers o to_string from_string =
1105 o.string_wrappers <- Some (to_string, from_string)
1107 let option_type o = (get_class o).class_name
1109 let once_value v = OnceValue v
1111 let restore_default o =
1112 o =:= o.option_default
1113 let set_option_desc o s =
1118 type option_info = {
1119 option_name : string;
1120 option_shortname : string;
1121 option_desc : string;
1122 option_value : string;
1123 option_help : string;
1124 option_advanced : bool;
1125 option_default : string;
1126 option_type : string;
1127 option_restart : bool;
1128 option_public : bool;
1129 option_internal : bool;
1134 let string_of_option_value o v =
1135 match o.option_class.string_wrappers with
1137 value_to_string (o.option_class.to_value v)
1138 | Some (to_string, _) -> to_string v
1140 let tuple2_to_value f x =
1141 let (v1, v2) = f x in
1144 let value_to_tuple2 f x =
1145 match value_to_list (fun id -> id) x with
1146 [v1;v2] -> f (v1, v2)
1149 let strings_of_option prefix o =
1150 match o.option_name with
1151 [] | _ :: _ :: _ -> failwith "Complex option"
1153 let desc = if o.option_desc = "" then name else o.option_desc in
1155 M.option_name = Printf.sprintf "%s%s" prefix name;
1156 M.option_shortname = name;
1157 M.option_desc = desc;
1158 M.option_value = string_of_option_value o o.option_value;
1159 M.option_default = string_of_option_value o o.option_default;
1160 M.option_advanced = o.option_advanced;
1161 M.option_help = o.option_help;
1162 M.option_type = o.option_class.class_name;
1163 M.option_restart = o.option_restart;
1164 M.option_public = o.option_public;
1165 M.option_internal = o.option_internal;
1168 let simple_options prefix opfile admin =
1169 let list = ref [] in
1173 if admin || o.option_public then
1174 try list := strings_of_option prefix o :: !list with _ -> ())
1176 opfile.file_sections;
1179 let simple_args prefix opfile =
1182 "-" ^ oi.M.option_name,
1185 lprintf_nl "Setting option %s" oi.M.option_name;
1186 set_simple_option opfile oi.M.option_name s),
1187 Printf.sprintf "<string> : \t%s (current: %s)"
1188 oi.M.option_help oi.M.option_value)
1189 (simple_options prefix opfile true)
1191 let prefixed_args prefix file =
1194 let s = String.sub s 1 (String.length s - 1) in
1195 Printf.sprintf "-%s:%s" prefix s, f, h)
1196 (simple_args "" file)
1198 let sections file = file.file_sections
1199 let section_name s = string_of_string_list s.section_name
1201 let strings_of_section_options prefix s =
1202 let list = ref [] in
1205 try list := strings_of_option prefix o :: !list with _ -> ())
1209 type option_info = M.option_info = {
1210 option_name : string;
1211 option_shortname : string;
1212 option_desc : string;
1213 option_value : string;
1214 option_help : string;
1215 option_advanced : bool;
1216 option_default : string;
1217 option_type : string;
1218 option_restart : bool;
1219 option_public : bool;
1220 option_internal : bool;
1223 let iter_section f s =
1224 List.iter f s.section_options
1226 let iter_file f file =
1227 List.iter (iter_section f) file.file_sections
1229 let strings_of_option o = strings_of_option "" o
1231 let set_after_load_hook file f =
1232 file.file_after_load_hook <- f
1234 let set_after_save_hook file f =
1235 file.file_after_save_hook <- f
1237 let set_before_save_hook file f =
1238 file.file_before_save_hook <- f