diff --git a/.gitignore b/.gitignore
index 405d29eb60..d410766660 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,6 @@ lib/gwlib.ml
# generated by Makefile
hd/etc/version.txt
+*~
+/_opam
+doc/doc
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 944b5a1db7..1c7f1376b0 100644
--- a/Makefile
+++ b/Makefile
@@ -13,8 +13,10 @@ endif
# Variables for packagers.
PREFIX=/usr
DISTRIB_DIR=distribution
-
BUILD_DIR=_build/default
+ODOC_DIR=$(BUILD_DIR)/_doc/_html
+
+DOC_DIR=doc
###### [BEGIN] Generated files section
@@ -174,8 +176,17 @@ distrib: build
doc: | $(GENERATED_FILES_DEP)
dune build @doc
+ if [ ! -d $(DOC_DIR)/doc ] ; then \
+ mkdir -p $(DOC_DIR)/doc ; \
+ cp -r $(ODOC_DIR)/* $(DOC_DIR)/doc ; \
+ fi
.PHONY: doc
+opendoc : doc
+ xdg-open $(DOC_DIR)/html/dev-doc/index.html
+.PHONY: opendoc
+
+
test: | $(GENERATED_FILES_DEP)
dune build @runtest
.PHONY: test
@@ -202,6 +213,7 @@ clean:
@echo -n "Cleaning..."
@$(RM) $(GENERATED_FILES_DEP) lib/*_piqi*.ml
@$(RM) -r $(DISTRIB_DIR)
+ @$(RM) -r $(DOC_DIR)/doc
@dune clean
@echo " Done!"
.PHONY: clean
diff --git a/bin/ged2gwb/ged2gwb.ml b/bin/ged2gwb/ged2gwb.ml
index 0c93aeab4f..4e72f4d7e7 100644
--- a/bin/ged2gwb/ged2gwb.ml
+++ b/bin/ged2gwb/ged2gwb.ml
@@ -2786,10 +2786,10 @@ let pass3 gen fname =
Some ('1'..'9') ->
Stream.junk strm__;
let (_ : string) = get_to_eoln 0 strm in loop ()
- | Some _ ->
+ | Some c ->
Stream.junk strm__;
print_location !line_cnt;
- Printf.fprintf !log_oc "Strange input.\n";
+ Printf.fprintf !log_oc "Strange input '%c' (%i).\n" c (Char.code c);
flush !log_oc;
let (_ : string) = get_to_eoln 0 strm in loop ()
| _ -> ()
diff --git a/bin/gwb2ged/gwb2gedLib.mli b/bin/gwb2ged/gwb2gedLib.mli
new file mode 100644
index 0000000000..902f929141
--- /dev/null
+++ b/bin/gwb2ged/gwb2gedLib.mli
@@ -0,0 +1,11 @@
+(** [gwb2ged with_indexes opts sel]
+ Converts a Geneweb database to a GEDCOM file.
+ * `with_indexes` specifies if indexes are printed or not;
+ * `opts` are the export options
+ * `sel` is a pair of selectors returned by the database export
+*)
+val gwb2ged :
+ bool ->
+ Gwexport.gwexport_opts ->
+ (Gwdb.iper -> bool) * (Gwdb.ifam -> bool)
+ -> unit
diff --git a/bin/gwc/db1link.ml b/bin/gwc/db1link.ml
index cbcd9f9d50..c437fdf6ef 100644
--- a/bin/gwc/db1link.ml
+++ b/bin/gwc/db1link.ml
@@ -7,18 +7,37 @@ open Def
(* From OCaml manual, integer in binary format is 4 bytes long. *)
let sizeof_long = 4
+(** Default source field for persons and families without source data *)
let default_source = ref ""
+
+(** Base consistency check *)
let do_check = ref true
+
+(** Compute consanguinity *)
let do_consang = ref false
+
+(** Print base's statistics *)
let pr_stats = ref false
+(** Extended person's entry in the base *)
type person = (int, int, int) Def.gen_person
+
+(** Person's ascendants entry in the base *)
type ascend = int Def.gen_ascend
+
+(** Person's union entry in the base *)
type union = int Def.gen_union
+
+(** Family's entry in the base *)
type family = (int, int, int) Def.gen_family
+
+(** Family's couple entry in the base *)
type couple = int Def.gen_couple
+
+(** Family's descendants entry in the base *)
type descend = int Def.gen_descend
+(** Restricted to the minimum [Def.gen_person] data type. *)
type ('person, 'string) gen_min_person =
{ mutable m_first_name : 'string;
mutable m_surname : 'string;
@@ -29,83 +48,159 @@ type ('person, 'string) gen_min_person =
mutable m_sex : sex;
mutable m_notes : 'string }
+(** Person's entry in the base *)
type min_person = (int, int) gen_min_person
+(** State of the base collecting all information at link time used to
+ create further Geneweb database *)
type cbase =
- { mutable c_persons : min_person array
+ {
+ (* Array of persons. Person at position [i] has corresponding to him [ascend]
+ and [union] at position [i] in [c_ascends] and [c_unions] respectively. *)
+ mutable c_persons : min_person array
+ (* Array of ascendants of persons *)
; mutable c_ascends : ascend array
+ (* Array of unions of persons *)
; mutable c_unions : union array
+ (* Array of families. Family at position [i] has corresponding to it [couple]
+ and [descend] at position [i] in [c_couples] and [c_descends] respectively. *)
; mutable c_families : family array
+ (** Array of couples of families *)
; mutable c_couples : couple array
+ (* Array of descendants of families *)
; mutable c_descends : descend array
+ (* Array of unique strings. Stores every string encoded information
+ (like person's name, bithplace, etc.) for other entries in the base. *)
; mutable c_strings : string array
+ (* Data base notes and extended page structure *)
; mutable c_bnotes : Def.base_notes
}
+(** Information about current .gwo file. *)
type file_info =
- { mutable f_curr_src_file : string
+ {
+ (* current .gw filename *)
+ mutable f_curr_src_file : string
+ (* current .gwo filename *)
; mutable f_curr_gwo_file : string
- ; mutable f_separate : bool
+ (* all persons from current file should be separated *)
+ ; mutable f_separate : bool
+ (* behavior for base notes from current file *)
; mutable f_bnotes : [ `merge | `erase | `first | `drop ]
+ (* shift all persons from the current file with the given number *)
; mutable f_shift : int
+ (* Table that associates person's names hash and its occurence number
+ with the index of person's entry inside the [base]. Contains only
+ persons from the local file. *)
; mutable f_local_names : (int * int, int) Hashtbl.t
}
+(** Global linker state *)
type gen =
- { mutable g_strings : (string, int) Hashtbl.t;
+ {
+ (* Table that associates unique string to its position inside
+ [g_base]'s unique string array *)
+ mutable g_strings : (string, int) Hashtbl.t;
+ (* Table that associates person's names hash with the index of
+ person's entry inside the [g_base].*)
mutable g_names : (int, int) Hashtbl.t;
+ (* Counter of persons inside [g_base] *)
mutable g_pcnt : int;
+ (* Counter of families inside [g_base] *)
mutable g_fcnt : int;
+ (* Counter of unique strings inside [g_base] *)
mutable g_scnt : int;
+ (* Current file info *)
g_file_info : file_info;
+ (* Base of collected information *)
g_base : cbase;
+ (* Wizard notes (wizard id and note's content) *)
mutable g_wiznotes : (string * string) list;
g_patch_p : (int, person) Hashtbl.t;
+ (** Array that for every person from [g_base] says if he
+ was defined before *)
mutable g_def : bool array;
+ (* Table that associates person's first and last name with
+ the next availiable occurence number for the person with
+ the same names.*)
g_first_av_occ : (string * string, int) Hashtbl.t;
+ (* Indicates if an error was occured *)
mutable g_errored : bool;
+ (* Temprary output chanel containing [g_pcnt] integers where [i]nth integer
+ corresponds to the position in [g_per] where [i]nth person is defined. *)
g_per_index : out_channel;
+ (* Temprary output chanel containing person's definition (or non-definition
+ marker) *)
g_per : out_channel;
+ (* Temprary output chanel containing [g_fcnt] integers where [i]nth integer
+ corresponds to the position in [g_fam] where [i]nth family is defined. *)
g_fam_index : out_channel;
+ (* Temprary output chanel containing family's definition *)
g_fam : out_channel }
+(** Set [gen.g_errored] telling that an error was occured *)
let check_error gen = gen.g_errored <- true
+(** Function that will be called if base's checker will find an error *)
let set_error base gen x =
Printf.printf "\nError: " ;
Check.print_base_error stdout base x ;
check_error gen
+(** Function that will be called if base's checker will find a warning *)
let set_warning base x =
Printf.printf "Warning: " ;
Check.print_base_warning stdout base x
+(** Returns person's entry from [base] at position [i] *)
let poi base i = base.c_persons.(i)
+
+(** Returns ascendant's entry from [base] at position [i] *)
let aoi base i = base.c_ascends.(i)
+
+(** Returns union's entry from [base] at position [i] *)
let uoi base i = base.c_unions.(i)
+
+(** Returns couple's entry from [base] at position [i] *)
let coi base i = base.c_couples.(i)
+
+(** Returns string in [base]'s unque string array at position [i] *)
let sou base i = base.c_strings.(i)
+
+(** Returns first name of a [base]'s person entry [p]. [p.m_first_name] contains
+ index where first name string representation is stored. *)
let p_first_name base p = Mutil.nominative (sou base p.m_first_name)
+
+(** Returns surname of a [base]'s person entry [p]. [p.m_first_name] contains
+ index where surname string representation is stored. *)
let p_surname base p = Mutil.nominative (sou base p.m_surname)
+
+(** Returns string designation of person {i firstname.occ surname}. *)
let designation base p =
let prenom = p_first_name base p in
let nom = p_surname base p in
prenom ^ "." ^ string_of_int p.m_occ ^ " " ^ nom
+(** Same as [Marshal.to_channel oc v [Marshal.No_sharing]] *)
let output_item_value oc v =
Marshal.to_channel oc v [Marshal.No_sharing]
+(** Same as [input_value] *)
let input_item_value ic =
input_value ic
-(**)
-
+(** Empty string *)
let no_string = ""
+(** Stores unique string (if not already present) inside the base's string array and
+ associate this string to its index in mentioned array. Extens array if needed.
+ Returns associated index. *)
let unique_string gen x =
try Hashtbl.find gen.g_strings x with
Not_found ->
+ (* string not found *)
if gen.g_scnt = Array.length gen.g_base.c_strings then
+ (* extend arrray of strings and copy previus elements *)
begin let arr = gen.g_base.c_strings in
let new_size = 2 * Array.length arr + 1 in
let new_arr = Array.make new_size no_string in
@@ -118,6 +213,7 @@ let unique_string gen x =
Hashtbl.add gen.g_strings x u;
u
+(** Dummy [family] with its empty [couple] and [descendants]. *)
let no_family gen =
let empty_string = unique_string gen "" in
let fam =
@@ -138,7 +234,10 @@ let no_family gen =
let des = {children = [| |]} in
fam, cpl, des
-let make_person gen p n occ =
+(** Initialises [min_person] with occurence number and index of [p] for first name and
+ index of [n] for surname in [base]. Other fields are initialised with default value.
+ Returns also empty [ascend] and [union] attached to the considered person. *)
+let make_person gen p n occ : min_person * ascend * union =
let empty_string = unique_string gen "" in
let p =
{m_first_name = unique_string gen p; m_surname = unique_string gen n;
@@ -148,8 +247,11 @@ let make_person gen p n occ =
and u = {family = [| |]} in
p, a, u
+(** Dummy [min_person] with its empty [ascend] and [union]. *)
let no_person gen = make_person gen "" "" 0
+(** Extends person's acendant's and union's arrays inside [gen.g_base]
+ if needed. *)
let new_iper gen =
if gen.g_pcnt = Array.length gen.g_base.c_persons then
let per_arr = gen.g_base.c_persons in
@@ -170,6 +272,8 @@ let new_iper gen =
Array.blit gen.g_def 0 new_def 0 (Array.length gen.g_def);
gen.g_def <- new_def
+(** Extends family's couple's and decendant's arrays inside [gen.g_base]
+ if needed. *)
let new_ifam gen =
if gen.g_fcnt = Array.length gen.g_base.c_families then
let fam_arr = gen.g_base.c_families in
@@ -187,23 +291,31 @@ let new_ifam gen =
Array.blit des_arr 0 new_des_arr 0 (Array.length des_arr);
gen.g_base.c_descends <- new_des_arr
+(** Convert [string Def.gen_title_name] to [int Def.gen_title_name].
+ If title is [Tname] stores title name as a string in the base. *)
let title_name_unique_string gen =
function
Tmain -> Tmain
| Tname n -> Tname (unique_string gen n)
| Tnone -> Tnone
+(** Convert [(string Def.gen_title] to [int Def.gen_title] and insert
+ all related to title information in the base. *)
let title_unique_string gen t =
{t_name = title_name_unique_string gen t.t_name;
t_ident = unique_string gen t.t_ident;
t_place = unique_string gen t.t_place; t_date_start = t.t_date_start;
t_date_end = t.t_date_end; t_nth = t.t_nth}
+(** Hash of person's first and last names. *)
let person_hash first_name surname =
let first_name = Mutil.nominative first_name in
let surname = Mutil.nominative surname in
let s = Name.crush_lower (first_name ^ " " ^ surname) in Hashtbl.hash s
+(** Returns index of a person's entry inside the [gen.base] that has the same
+ first name, surname and occurence number. Raises [Not_found] if person
+ is not found. *)
let find_person_by_global_name gen first_name surname occ =
let first_name = Mutil.nominative first_name in
let surname = Mutil.nominative surname in
@@ -217,6 +329,8 @@ let find_person_by_global_name gen first_name surname occ =
[] -> raise Not_found
| ip :: ipl ->
let p = poi gen.g_base ip in
+ (* refine search by fullnames comparison (without crushlower) and with
+ occurence comparison *)
if p.m_occ = occ &&
Name.lower (p_first_name gen.g_base p) = first_name &&
Name.lower (p_surname gen.g_base p) = surname
@@ -226,6 +340,9 @@ let find_person_by_global_name gen first_name surname occ =
in
loop ipl
+(** Returns index of a person's entry inside the [gen.base] that has the same
+ first name, surname and occurence number. Searches only persons defined
+ in the current file. Raises [Not_found] if person is not found. *)
let find_person_by_local_name gen first_name surname occ =
let first_name = Mutil.nominative first_name in
let surname = Mutil.nominative surname in
@@ -239,6 +356,7 @@ let find_person_by_local_name gen first_name surname occ =
[] -> raise Not_found
| ip :: ipl ->
let p = poi gen.g_base ip in
+ (* refine search by fullnames comparison (without crushlower) *)
if Name.lower (p_first_name gen.g_base p) = first_name &&
Name.lower (p_surname gen.g_base p) = surname
then
@@ -247,15 +365,24 @@ let find_person_by_local_name gen first_name surname occ =
in
loop ipl
+(** Returns index of a person's entry inside the [gen.base] that has the same
+ first name, surname and occurence number. Calls [find_person_by_local_name]
+ if option [f_separate] is enabled for the current file, otherwise calls
+ [find_person_by_global_name]. Raises [Not_found] if person is not found. *)
let find_person_by_name gen first_name surname occ =
if gen.g_file_info.f_separate then
find_person_by_local_name gen first_name surname occ
else find_person_by_global_name gen first_name surname occ
+(** Add entry in the global names table [gen.g_names] for the giving
+ first and last names associated to the index of their person's entry
+ in [base]. *)
let add_person_by_name gen first_name surname int =
let s = Name.crush_lower (Mutil.nominative (first_name ^ " " ^ surname)) in
let key = Hashtbl.hash s in Hashtbl.add gen.g_names key int
+(** Returns first available occurence number that is >= [occ] for the person
+ with the giving information. *)
let find_first_available_occ gen fn sn occ =
let occ =
try max occ (Hashtbl.find gen.g_first_av_occ (fn, sn)) with
@@ -271,8 +398,31 @@ let find_first_available_occ gen fn sn occ =
in
loop occ
+(** Insert person's reference in the base and modifies all coresponding
+ fields in [gen] and returns its entry and entry's index in the base.
+ In details:
+
+ - if considered person doesn't exists in the base (wasn't defined or
+ referenced before) then function:
+
+ - maps its key (names and occurence number) within the varius
+ hash tables
+ - creates entry (of type [min_gen]) for the giving person and his
+ ascendants and union in the base.
+ - initialises its entry (with key information)
+ - stores marker in the [gen.g_per] channel telling that person
+ wasn't defined.
+ - stores in [gen.g_per_index] position where marker was stored in
+ [gen.g_per].
+
+ - if considered person was referenced or defined before then doesn't do
+ anything (just returns its entry and entry's index in the base)
+
+ *)
let insert_undefined gen key =
+ (* shift person's occurence *)
let occ = key.pk_occ + gen.g_file_info.f_shift in
+ (* person with its position in the base *)
let (x, ip) =
try
if key.pk_first_name = "?" || key.pk_surname = "?" then raise Not_found
@@ -281,7 +431,9 @@ let insert_undefined gen key =
find_person_by_name gen key.pk_first_name key.pk_surname occ
in
poi gen.g_base ip, ip
+ (* if person not found *)
with Not_found ->
+ (* abailable occurence number *)
let new_occ =
if gen.g_file_info.f_separate && key.pk_first_name <> "?" &&
key.pk_surname <> "?"
@@ -289,28 +441,37 @@ let insert_undefined gen key =
find_first_available_occ gen key.pk_first_name key.pk_surname occ
else occ
in
+ (* person's entry index *)
let i = gen.g_pcnt in
let (x, a, u) =
make_person gen key.pk_first_name key.pk_surname new_occ
in
+ (* strore names globally *)
if key.pk_first_name <> "?" && key.pk_surname <> "?" then
add_person_by_name gen key.pk_first_name key.pk_surname
(i)
else if !(Gwcomp.create_all_keys) then
add_person_by_name gen key.pk_first_name key.pk_surname
(i);
+ (* extend arrays if needed *)
new_iper gen;
+ (* add person to array *)
gen.g_base.c_persons.(i) <- x;
+ (* add associated to person ascendants to array *)
gen.g_base.c_ascends.(i) <- a;
+ (* add associated to person union to array *)
gen.g_base.c_unions.(i) <- u;
gen.g_pcnt <- gen.g_pcnt + 1;
+ (* strore names locally *)
if key.pk_first_name <> "?" && key.pk_surname <> "?" then
begin let h = person_hash key.pk_first_name key.pk_surname in
Hashtbl.add gen.g_file_info.f_local_names (h, occ)
(i)
end;
+ (* write start position of person in [g_per] *)
seek_out gen.g_per_index (sizeof_long * i);
output_binary_int gen.g_per_index (pos_out gen.g_per);
+ (* write marker *)
output_char gen.g_per 'U';
x, i
in
@@ -335,15 +496,49 @@ let insert_undefined gen key =
end;
x, ip
+(** Insert person's definition in the base and modifies all coresponding
+ fields in [gen] and returns its entry and entry's index in the base.
+ In details:
+
+ - if considered person doesn't exists in the base (wasn't defined or
+ referenced before) then function:
+
+ - maps its key (names and occurence number) within the varius
+ hash tables
+ - creates entry (of type [min_gen]) for the giving person and his
+ ascendants and union in the base.
+ - initialises its entry (with key information)
+ - marks it as defined
+ - convert [(_,_,string) gen_person] to [person] (sex, events, titles
+ and related persons stays uninitialised) and stores it in the
+ [gen.g_per] channel.
+ - stores in [gen.g_per_index] position where person was stored in
+ [gen.g_per].
+
+ - if considered person was referenced before (but not defined) then
+ function:
+
+ - get person's entry and its index from the base
+ - marks it as defined
+ - convert [(_,_,string) gen_person] to [person] (sex, events and
+ related persons stays uninitialised) and stores it in the
+ [gen.g_per] channel.
+ - updates previus index in [gen.g_per_index] in order to point to
+ the definition instead of pointing to the reference.
+*)
let insert_person gen so =
+ (* shift person's occurence *)
let occ = so.occ + gen.g_file_info.f_shift in
+ (* person with its position in the base *)
let (x, ip) =
try
if so.first_name = "?" || so.surname = "?" then raise Not_found
else
let ip = find_person_by_name gen so.first_name so.surname occ in
poi gen.g_base ip, ip
+ (* if person not found *)
with Not_found ->
+ (* abailable occurence number *)
let new_occ =
if gen.g_file_info.f_separate && so.first_name <> "?" &&
so.surname <> "?"
@@ -351,19 +546,26 @@ let insert_person gen so =
find_first_available_occ gen so.first_name so.surname occ
else occ
in
+ (* person's entry index *)
let i = gen.g_pcnt in
let (x, a, u) = make_person gen so.first_name so.surname new_occ in
+ (* strore names globally *)
if so.first_name <> "?" && so.surname <> "?" then
add_person_by_name gen so.first_name so.surname
(i)
else if !(Gwcomp.create_all_keys) then
add_person_by_name gen so.first_name so.surname
(i);
+ (* extend arrays if needed *)
new_iper gen;
+ (* add person to array *)
gen.g_base.c_persons.(i) <- x;
+ (* add associated to person ascendants to array *)
gen.g_base.c_ascends.(i) <- a;
+ (* add associated to person union to array *)
gen.g_base.c_unions.(i) <- u;
gen.g_pcnt <- gen.g_pcnt + 1;
+ (* strore names locally *)
if so.first_name <> "?" && so.surname <> "?" then
begin let h = person_hash so.first_name so.surname in
Hashtbl.add gen.g_file_info.f_local_names (h, occ)
@@ -371,8 +573,10 @@ let insert_person gen so =
end;
x, i
in
+ (* if person wad defined before (not just referenced) *)
if gen.g_def.(ip) then
begin
+ (* print error about person beeing already defined *)
Printf.printf "\nPerson already defined: \"%s%s %s\"\n" so.first_name
(match x.m_occ with
0 -> ""
@@ -389,11 +593,13 @@ let insert_person gen so =
flush stdout;
check_error gen
end
+ (* else set it as defined *)
else gen.g_def.(ip) <- true;
if not gen.g_errored then
if sou gen.g_base x.m_first_name <> so.first_name ||
sou gen.g_base x.m_surname <> so.surname
then
+ (* print error about person defined with two spellings *)
begin
Printf.printf "\nPerson defined with two spellings:\n";
Printf.printf " \"%s%s %s\"\n" so.first_name
@@ -411,6 +617,7 @@ let insert_person gen so =
end;
if not gen.g_errored then
begin let empty_string = unique_string gen "" in
+ (* Convert [(_,_,string) gen_person] to [person]. Save all strings in base *)
let x =
{first_name = empty_string; surname = empty_string; occ = 0;
image = unique_string gen so.image;
@@ -441,18 +648,24 @@ let insert_person gen so =
(if so.psources = "" then !default_source else so.psources);
key_index = ip}
in
+ (* write/update start position of person in [g_per] *)
seek_out gen.g_per_index (sizeof_long * ip);
output_binary_int gen.g_per_index (pos_out gen.g_per);
+ (* write person *)
output_char gen.g_per 'D';
output_item_value gen.g_per (x : person)
end;
x, ip
+(** Insert definition or reference in [gen] and returns its entry and
+ entry's index in the [gen.g_base]. Calls [insert_person] for definition
+ and [insert_undefined] for reference. *)
let insert_somebody gen =
function
Undefined key -> insert_undefined gen key
| Defined so -> insert_person gen so
+(** Checks if childran [ix] doesn't have another parents *)
let check_parents_not_already_defined gen ix fath moth =
let x = poi gen.g_base ix in
match (aoi gen.g_base ix).parents with
@@ -478,12 +691,16 @@ let check_parents_not_already_defined gen ix fath moth =
check_error gen
| _ -> ()
+(** Assign sex to the person's entry if it's unitialised.
+ Print message if sexes are different. *)
let notice_sex gen p s =
if p.m_sex = Neuter then p.m_sex <- s
else if p.m_sex <> s && s <> Neuter then
Printf.printf "\nInconsistency about the sex of\n %s %s\n"
(p_first_name gen.g_base p) (p_surname gen.g_base p)
+(** Convert [string Def.gen_fam_event_name] to [int Def.gen_fam_event_name].
+ If event is [Efam_Name] stores event name as a string in the base. *)
let fevent_name_unique_string gen =
function
Efam_Marriage | Efam_NoMarriage | Efam_NoMention | Efam_Engage |
@@ -493,6 +710,7 @@ let fevent_name_unique_string gen =
evt
| Efam_Name n -> Efam_Name (unique_string gen n)
+(** Update family by looking up information inferred from family events *)
let update_family_with_fevents gen fam =
let found_marriage = ref false in
let found_divorce = ref false in
@@ -595,6 +813,7 @@ let update_family_with_fevents gen fam =
in
loop (List.rev fam.fevents) fam
+(** Update family event list by looking up inferred family information. *)
let update_fevents_with_family gen fam =
let empty_string = 0 in
let evt_marr =
@@ -654,6 +873,24 @@ let update_fevents_with_family gen fam =
in
{fam with fevents = fevents}
+(** Insert family in the base and modifies all coresponding
+ fields in [gen] and returns its entry and entry's index in the base.
+ In details function does:
+
+ - inserts father and mother in the person's base
+ - insert every witness in the person's base and associate father
+ as a related person.
+ - order, convert, adjust and insert events
+ - insert every childran in the person's base
+ - creates entry (of type [family]) for the giving family and its
+ couple and descendants in the base.
+ - associate father's and mother's union to the current family
+ - associate every childran's ascendants to the current family
+ (current couple, since it has the same index)
+ - stores family in the [gen.g_fam] channel.
+ - stores in [gen.g_fam_index] position where person was stored in
+ [gen.g_index].
+*)
let insert_family gen co fath_sex moth_sex witl fevtl fo deo =
let fath, ifath, moth, imoth =
match insert_somebody gen (Adef.father co), insert_somebody gen (Adef.mother co) with
@@ -662,23 +899,29 @@ let insert_family gen co fath_sex moth_sex witl fevtl fo deo =
(moth, imoth, fath, ifath)
| ((fath, ifath), (moth, imoth)) -> (fath, ifath, moth, imoth)
in
+ (* insert all family witnesses *)
let witl =
List.map
(fun (wit, sex) ->
let (p, ip) = insert_somebody gen wit in
- notice_sex gen p sex; p.m_related <- ifath :: p.m_related; ip)
+ notice_sex gen p sex;
+ (* add father to witness' related persons *)
+ p.m_related <- ifath :: p.m_related;
+ ip)
witl
in
- (* On tri les évènements pour être sûr. *)
+ (* Events are sorted by chronological order (if equal then by alphabetical order) *)
let fevents =
CheckItem.sort_events
(fun (name, _, _, _, _, _, _) -> CheckItem.Fsort name)
(fun (_, date, _, _, _, _, _) -> date)
fevtl
in
+ (* Create [int Def.gen_fam_event_name] list from [fevents]*)
let fevents =
List.map
(fun (name, date, place, reason, src, notes, witl) ->
+ (* insert all event witnesses *)
let witnesses =
List.map
(fun (wit, sex, wk) ->
@@ -696,19 +939,25 @@ let insert_family gen co fath_sex moth_sex witl fevtl fo deo =
efam_witnesses = Array.of_list witnesses})
fevents
in
+ (* insert all children *)
let children =
Array.map
(fun key ->
let (e, ie) = insert_person gen key in notice_sex gen e key.sex; ie)
deo.children
in
+ (* insert family comment *)
let comment = unique_string gen fo.comment in
+ (* insert sources comment *)
let fsources =
unique_string gen
(if fo.fsources = "" then !default_source else fo.fsources)
in
+ (* extend arrays if needed *)
new_ifam gen;
+ (* family's entry index *)
let i = gen.g_fcnt in
+ (* Convert [(_,_,string) gen_family] to [family]. Save all strings in base *)
let fam =
{marriage = fo.marriage;
marriage_place = unique_string gen fo.marriage_place;
@@ -718,22 +967,32 @@ let insert_family gen co fath_sex moth_sex witl fevtl fo deo =
divorce = fo.divorce; fevents = fevents; comment = comment;
origin_file = unique_string gen fo.origin_file; fsources = fsources;
fam_index = i}
+ (* create couple *)
and cpl = Adef.couple ifath imoth
+ (* created descandants *)
and des = {children = children} in
(* On mets à jour les fevents et events normaux *)
let fam =
if fevents <> [] then update_family_with_fevents gen fam
else update_fevents_with_family gen fam
in
+ (* father's union *)
let fath_uni = uoi gen.g_base ifath in
+ (* mother's union *)
let moth_uni = uoi gen.g_base imoth in
+ (* write start position of family in [g_fam] *)
seek_out gen.g_fam_index (sizeof_long * i);
output_binary_int gen.g_fam_index (pos_out gen.g_fam);
+ (* write family *)
output_item_value gen.g_fam (fam : family);
+ (* add family to array *)
gen.g_base.c_families.(gen.g_fcnt) <- fam;
+ (* add couple to array *)
gen.g_base.c_couples.(gen.g_fcnt) <- cpl;
+ (* add descendants to array *)
gen.g_base.c_descends.(gen.g_fcnt) <- des;
gen.g_fcnt <- gen.g_fcnt + 1;
+ (* append this family to father's and mother's union *)
let fath_uni =
{family = Array.append fath_uni.family [| i |]}
in
@@ -744,14 +1003,18 @@ let insert_family gen co fath_sex moth_sex witl fevtl fo deo =
gen.g_base.c_unions.(imoth) <- moth_uni;
notice_sex gen fath fath_sex;
notice_sex gen moth moth_sex;
+ (* Append familly to the childran's ascendant *)
Array.iter
(fun ix ->
let a = gen.g_base.c_ascends.(ix) in
+ (* check if childran has no another parents *)
check_parents_not_already_defined gen ix fath moth;
let a = {a with parents = Some (i)} in
gen.g_base.c_ascends.(ix) <- a)
children
+(** Convert [string Def.gen_pers_event_name] to [int Def.gen_pers_event_name].
+ If event is [Epers_Name] stores event name as a string in the base. *)
let pevent_name_unique_string gen =
function
Epers_Birth | Epers_Baptism | Epers_Death | Epers_Burial |
@@ -773,7 +1036,10 @@ let pevent_name_unique_string gen =
evt
| Epers_Name n -> Epers_Name (unique_string gen n)
+(** Insert all related to the event information and add it to the person's entry
+ in the [gen.g_base] *)
let insert_pevents fname gen sb pevtl =
+ (* insert concered person *)
let (p, ip) = insert_somebody gen sb in
if p.m_pevents <> [] then
begin
@@ -785,20 +1051,24 @@ let insert_pevents fname gen sb pevtl =
check_error gen
end
else
+ (* sort evenets *)
let pevents =
CheckItem.sort_events
(fun (name, _, _, _, _, _, _) -> CheckItem.Psort name)
(fun (_, date, _, _, _, _, _) -> date)
pevtl
in
+ (* convert evenets. Insert all related to evenet information in the base *)
let pevents =
List.map
(fun (name, date, place, reason, src, notes, witl) ->
let witnesses =
List.map
(fun (wit, sex, wk) ->
+ (* insert witnesses *)
let (wp, wip) = insert_somebody gen wit in
notice_sex gen wp sex;
+ (* add concerned person as witness' relation *)
wp.m_related <- ip :: wp.m_related;
wip, wk)
witl
@@ -811,8 +1081,11 @@ let insert_pevents fname gen sb pevtl =
epers_witnesses = Array.of_list witnesses})
pevents
in
+ (* add events to the person's entry in the base *)
p.m_pevents <- pevents
+(** Insert person's notes in the base and associate it to the referenced
+ with [key] person *)
let insert_notes fname gen key str =
let occ = key.pk_occ + gen.g_file_info.f_shift in
match
@@ -836,9 +1109,14 @@ let insert_notes fname gen key str =
(if occ = 0 then "" else "." ^ string_of_int occ) key.pk_surname;
flush stdout
+(** Changes [gen.g_base.c_bnotes] to take into account [nfname] page
+ and its content [str] that is treated by the way mentioned in
+ [gen.g_file_info.f_bnotes]. *)
let insert_bnotes fname gen nfname str =
if gen.g_file_info.f_bnotes <> `drop then begin
let old_nread = gen.g_base.c_bnotes.nread in
+ (* Convert path notation from 'dir1:dir2:file' to 'dir1/dir2/file'
+ (if a valid path) *)
let nfname =
if nfname = "" then ""
else
@@ -868,6 +1146,7 @@ let insert_bnotes fname gen nfname str =
gen.g_base.c_bnotes <- bnotes
end
+(** Add wizard and his note to the [gen] *)
let insert_wiznote gen wizid str =
gen.g_wiznotes <- (wizid, str) :: gen.g_wiznotes
@@ -876,19 +1155,27 @@ let map_option f =
Some x -> Some (f x)
| None -> None
+(** Insert parent in the base and adjust his sex if needed. Concerned
+ person is added in the list of parent's related persons. *)
let insert_relation_parent gen ip s k =
let (par, ipar) = insert_somebody gen k in
par.m_related <- ip :: par.m_related;
if par.m_sex = Neuter then par.m_sex <- s;
ipar
+(** Convert [(Dune__exe.Gwcomp.somebody, string) Def.gen_relation] to
+ [(int, int) Def.gen_relation] and insert all related to relation
+ information in the base. *)
let insert_relation gen ip r =
{r_type = r.r_type;
r_fath = map_option (insert_relation_parent gen ip Male) r.r_fath;
r_moth = map_option (insert_relation_parent gen ip Female) r.r_moth;
r_sources = unique_string gen r.r_sources}
+(** Insert all information related to the person's relations and add those
+ relations to the person's list of related parents. *)
let insert_relations fname gen sb sex rl =
+ (* insert concerned person *)
let (p, ip) = insert_somebody gen sb in
if p.m_rparents <> [] then
begin
@@ -905,6 +1192,7 @@ let insert_relations fname gen sb sex rl =
let rl = List.map (insert_relation gen ip) rl in p.m_rparents <- rl
end
+(** Insert syntax element read from .gwo file. *)
let insert_syntax fname gen = function
| Family (cpl, fs, ms, witl, fevents, fam, des) ->
insert_family gen cpl fs ms witl fevents fam des
@@ -914,6 +1202,7 @@ let insert_syntax fname gen = function
| Bnotes (nfname, str) -> insert_bnotes fname gen nfname str
| Wnotes (wizid, str) -> insert_wiznote gen wizid str
+(** Update person by looking up information inferred from person events *)
let update_person_with_pevents p =
let found_birth = ref false in
let found_baptism = ref false in
@@ -987,6 +1276,7 @@ let update_person_with_pevents p =
in
loop p.pevents p
+(** Update person's event list by looking up inferred personal information. *)
let update_pevents_with_person p =
let empty_string = 0 in
let evt_birth =
@@ -1113,6 +1403,9 @@ let update_pevents_with_person p =
in
{p with pevents = pevents}
+(** Returns list of persons from [min_person list] where some absent information
+ was extracted from file [per_ic]. Adjusts person with inffered information
+ from events or inversely. *)
let convert_persons per_index_ic per_ic persons =
Array.mapi begin fun i mp ->
let p =
@@ -1124,7 +1417,9 @@ let convert_persons per_index_ic per_ic persons =
with End_of_file -> 'U'
in
match c with
+ (* if person is defined read person *)
| 'D' -> (input_item_value per_ic : person)
+ (* if read person is undefined then create dummy person *)
| 'U' ->
let empty_string = 0 in
{first_name = empty_string; surname = empty_string; occ = 0;
@@ -1155,6 +1450,7 @@ let convert_persons per_index_ic per_ic persons =
else update_pevents_with_person p
end persons
+(** File containing the particles to use *)
let particules_file = ref ""
let convert_families fam_index_ic fam_ic len =
@@ -1165,10 +1461,13 @@ let convert_families fam_index_ic fam_ic len =
(input_item_value fam_ic : family)
end
+(** Returns list of particles from the file. If filename is empty string
+ then returns default particles list *)
let input_particles = function
| "" -> Mutil.default_particles
| file -> Mutil.input_particles file
+(** Empty base *)
let empty_base : cbase =
{ c_persons = [| |]
; c_ascends = [| |]
@@ -1181,12 +1480,14 @@ let empty_base : cbase =
; norigin_file = ""
; efiles = fun _ -> [] } }
+(** Extract information from the [gen.g_base] and create database *)
let make_base bname gen per_index_ic per_ic =
let _ =
Printf.eprintf "pcnt %d persons %d\n" gen.g_pcnt
(Array.length gen.g_base.c_persons);
flush stderr
in
+ (* get full persons information *)
let persons =
let a = Array.sub gen.g_base.c_persons 0 gen.g_pcnt in
gen.g_base.c_persons <- [| |];
@@ -1230,9 +1531,11 @@ let make_base bname gen per_index_ic per_ic =
((persons, ascends, unions), (families, couples, descends), strings
, gen.g_base.c_bnotes)
+(** Write content in the file *)
let write_file_contents fname text =
let oc = open_out fname in output_string oc text; close_out oc
+(** Create and fill a file for every wizard note *)
let output_wizard_notes bdir wiznotes =
let wizdir = Filename.concat bdir "wiznotes" in
if wiznotes <> [] then begin
@@ -1244,6 +1547,7 @@ let output_wizard_notes bdir wiznotes =
end wiznotes
end
+(** Create file that contains command used to call this program *)
let output_command_line bdir =
let oc = open_out (Filename.concat bdir "command.txt") in
Printf.fprintf oc "%s" Sys.argv.(0);
@@ -1253,6 +1557,7 @@ let output_command_line bdir =
Printf.fprintf oc "\n";
close_out oc
+(** Link .gwo files and create a database. *)
let link next_family_fun bdir =
let tmp_dir = Filename.concat "gw_tmp" bdir in
Mutil.mkdir_p tmp_dir ;
diff --git a/bin/gwc/db1link.mli b/bin/gwc/db1link.mli
new file mode 100644
index 0000000000..86988b40ab
--- /dev/null
+++ b/bin/gwc/db1link.mli
@@ -0,0 +1,28 @@
+(** Default source field for persons and families without source data *)
+val default_source : string ref
+
+(** Base consistency check *)
+val do_check : bool ref
+
+(** Compute consanguinity *)
+val do_consang : bool ref
+
+(** Print base's statistics *)
+val pr_stats : bool ref
+
+(** File containing the particles to use *)
+val particules_file : string ref
+
+(** Information about current .gwo file. *)
+type file_info = {
+ mutable f_curr_src_file : string;
+ mutable f_curr_gwo_file : string;
+ mutable f_separate : bool;
+ mutable f_bnotes : [ `drop | `erase | `first | `merge ];
+ mutable f_shift : int;
+ mutable f_local_names : (int * int, int) Hashtbl.t;
+}
+
+(** Link .gwo files and create a database. *)
+val link :
+ (file_info -> unit -> Gwcomp.gw_syntax option) -> string -> bool
diff --git a/bin/gwc/gwc.ml b/bin/gwc/gwc.ml
index ecfb8ab8c3..c6025c9b98 100644
--- a/bin/gwc/gwc.ml
+++ b/bin/gwc/gwc.ml
@@ -3,6 +3,7 @@
open Geneweb
open Gwcomp
+(** Checks a .gwo header and prints fails if header is absent or not compatible. *)
let check_magic fname ic =
let b = really_input_string ic (String.length magic_gwo) in
if b <> magic_gwo then
@@ -14,6 +15,17 @@ let check_magic fname ic =
("\"" ^ fname ^
"\" is not a GeneWeb object file, or it is a very old version")
+(** [next_family_fun_templ gwo_list fi] creates a function that read
+ sucessivly a [Gwcomp.gw_syntax] for all .gwo files. In details it does :
+
+ - Switch to the next element in the [gwo_list] if reached the end
+ of the current file. Each element is [(gwo,separate, bnotes, shift)]
+ where [gwo] is .gwo filename and [separate], [bnotes], [shift] are
+ captured options from command line related to the giving file.
+ - Modify [fi] with mentioned previusly information if needed.
+ - Start/continue to read current .gwo file content and return
+ [Gwcomp.gw_syntax]. [None] is returned when reading of the last
+ .gwo file reaches end of file *)
let next_family_fun_templ gwo_list fi =
let ngwo = List.length gwo_list in
let run =
@@ -49,6 +61,7 @@ let next_family_fun_templ gwo_list fi =
match r with
Some fam -> Some fam
| None ->
+ (* switch to the next .gwo file *)
match !gwo_list with
(x, separate, bnotes, shift) :: rest ->
run ();
diff --git a/bin/gwc/gwcomp.ml b/bin/gwc/gwcomp.ml
index f99efc8f48..402060736e 100644
--- a/bin/gwc/gwcomp.ml
+++ b/bin/gwc/gwcomp.ml
@@ -4,38 +4,77 @@ open Geneweb
open Def
open Gwdb
+(** .gwo file header *)
let magic_gwo = "GnWo000o"
(* Option qui force a créé les clés des individus. De fait, *)
(* si la clé est incomplète, on l'enregistre tout de même. *)
let create_all_keys = ref false
+(** Key to refer a person's definition *)
type key = { pk_first_name : string; pk_surname : string; pk_occ : int }
+(** Represents a person in .gw file. It could be either reference to a person
+ (only key elements provided) or definition (all information provided). *)
type somebody =
- Undefined of key
+ (** Reference to person *)
+ | Undefined of key
+ (** Person's definition *)
| Defined of (iper, iper, string) gen_person
+(** Blocks that could appear in .gw file. *)
type gw_syntax =
- Family of
- somebody gen_couple * sex * sex * (somebody * sex) list *
- (string gen_fam_event_name * cdate * string * string * string *
- string * (somebody * sex * witness_kind) list)
- list *
- ((iper, iper, string) gen_person, ifam, string) gen_family *
- (iper, iper, string) gen_person gen_descend
+ (** Family definition block. Contains:
+ - Family couple (father's and mother's definition/reference)
+ - Father's sex
+ - Mother's sex
+ - List of witnesses definition/reference with their sex.
+ - List of information about every family event (name, date,
+ place, reason, source, notes and witnesses)
+ - Family definition
+ - Children (descendants) *)
+ | Family of
+ somebody gen_couple
+ * sex
+ * sex
+ * (somebody * sex) list
+ * (string gen_fam_event_name * cdate * string * string * string *
+ string * (somebody * sex * witness_kind) list) list
+ * ((iper, iper, string) gen_person, ifam, string) gen_family
+ * (iper, iper, string) gen_person gen_descend
+ (** Block that defines personal notes. First element represents
+ reference to person. Second is note's content. *)
| Notes of key * string
+ (** Block that defines relations of a person with someone outisde of
+ family block (like foster parents) (field {i rparents}). Contains:
+ - Concerned person definition/reference
+ - Sex of person
+ - List of his relations. *)
| Relations of somebody * sex * (somebody, string) gen_relation list
+ (** Block that defines events of a person. Specific to gwplus format. Contains:
+ - Concerned person's definition/reference
+ - Sex of person
+ - List of information about every personal event (name, date,
+ place, reason, source, notes and witnesses)*)
| Pevent of
- somebody * sex *
- (string gen_pers_event_name * cdate * string * string * string *
- string * (somebody * sex * witness_kind) list)
- list
+ somebody
+ * sex
+ * (string gen_pers_event_name * cdate * string * string * string *
+ string * (somebody * sex * witness_kind) list) list
+ (** Block that defines database notes and extended pages.
+ First string represents name of extended page ("" for
+ database notes, only one for file). Second is note's
+ or page's content. *)
| Bnotes of string * string
+ (** Block that defines wizard notes. First string represents
+ First string represents wizard's id. Second is note's content. *)
| Wnotes of string * string
+(** {i .gw} file encoding *)
type encoding = E_utf_8 | E_iso_8859_1
+(** [copy_decode s i1 i2] decode the word delimited by [i1] and [i2] inside [s]
+ by remplacing "\\" -> '\' and '_' -> ' ' *)
let copy_decode s i1 i2 =
let len =
let rec loop len i =
@@ -61,6 +100,7 @@ let copy_decode s i1 i2 =
in
loop_copy (Bytes.create len) i1 0
+(** Return list of words inside the [str] *)
let fields str =
let rec loop beg i =
if i < String.length str then
@@ -74,6 +114,7 @@ let fields str =
in
loop 0 0
+(** Removes spaces at the begining an at the end of string. *)
let cut_space x =
let len = String.length x in
if len = 0 then x
@@ -83,11 +124,14 @@ let cut_space x =
let stop = if x.[len-1] = ' ' then len - 1 else len in
if start = 0 && stop = len then x else String.sub x start (stop - start)
+(** Returns field if its label [lab] is first element of [l] *)
let get_field lab l =
match l with
lab1 :: x :: l' when lab1 = lab -> cut_space x, l'
| _ -> "", l
+(** Parses [Def.date] from string that starts at pos [i]
+ inside [s] *)
let date_of_string s i =
let champ i =
let (neg, i) =
@@ -229,10 +273,17 @@ let date_of_string s i =
Some (dt, i) -> if i = String.length s then Some dt else error 5
| None -> None
+(** Line counter while reading .gw file *)
let line_cnt = ref 0
+
+(** Do not raise exception if syntax error occured.
+ Instead print error information on stdout *)
let no_fail = ref false
+
+(** Save path to the images *)
let no_picture = ref false
+(** Read line from input channel. *)
let input_line0 ic =
let line = input_line ic in
incr line_cnt;
@@ -240,16 +291,19 @@ let input_line0 ic =
String.sub line 0 (String.length line - 1)
else line
+(** Read a line and convert it to [encoding]. *)
let input_a_line (ic, encoding) =
let line = input_line0 ic in
match encoding with
E_utf_8 -> line
| E_iso_8859_1 -> Mutil.utf_8_of_iso_8859_1 line
+(** Read a line. If line is empty or only contains a comment, then read next line *)
let rec input_real_line ic =
let x = input_a_line ic in
if x = "" || x.[0] = '#' then input_real_line ic else x
+(** Parses person's birthdate if it is present. *)
let get_optional_birthdate l =
match l with
x :: l' ->
@@ -263,6 +317,7 @@ let get_optional_birthdate l =
end
| _ -> None, l
+(** Parses person's babtization date if it is present. *)
let get_optional_baptdate l =
match l with
x :: l' ->
@@ -276,6 +331,7 @@ let get_optional_baptdate l =
else None, l
| _ -> None, l
+(** Parse death information if present. *)
let get_optional_deathdate l =
match l with
"?" :: l' -> Some DontKnowIfDead, l'
@@ -304,6 +360,7 @@ let get_optional_deathdate l =
else None, l
| _ -> None, l
+(** Parse burial information if present. *)
let get_burial l =
match l with
"#buri" :: l ->
@@ -332,12 +389,15 @@ let get_burial l =
end
| _ -> UnknownBurial, l
+(** Parse sex of person *)
let get_optional_sexe =
function
"h" :: l -> Male, l
| "f" :: l -> Female, l
| l -> Neuter, l
+(** Parses int from that starts at the position [i] inside [x].
+ Raises [Not_found] if integer isn't found. *)
let make_int x =
let rec loop found n i =
if i = String.length x then if found then n else raise Not_found
@@ -349,6 +409,8 @@ let make_int x =
in
loop false 0
+(** Parses person's first name and occurence number. Returns 0 if occurence
+ number isn't present. *)
let get_fst_name str l =
match l with
x :: l' ->
@@ -366,10 +428,11 @@ let get_fst_name str l =
| None -> x, 0
in
x, occ, l'
- | _ -> failwith str
+ | _ -> failwith str
end
- | _ -> failwith str
+ | _ -> failwith str
+(** Parses person's first name aliases if they are present *)
let rec get_fst_names_aliases str l =
match l with
x :: l' ->
@@ -379,24 +442,31 @@ let rec get_fst_names_aliases str l =
else [], l
| [] -> [], l
+(** Parses person's surname aliases if they are present *)
let rec get_surnames_aliases str l =
match l with
"#salias" :: x :: l' ->
let (nl, l) = get_surnames_aliases str l' in cut_space x :: nl, l
| _ -> [], l
+(** Parses person's qualifiers if they are present *)
let rec get_qualifiers str l =
match l with
"#nick" :: x :: l' ->
let (nl, l) = get_qualifiers str l' in cut_space x :: nl, l
| _ -> [], l
+(** Parses person's aliases if they are present *)
let rec get_aliases str l =
match l with
"#alias" :: x :: l' ->
let (nl, l) = get_aliases str l' in cut_space x :: nl, l
| _ -> [], l
+(** [get_name l] parses a last name. Looks up first element of the list and returns a
+ [(name,rest)] couple where [name] is a person's last name and [rest] is a tail of
+ the list. If first element is [#nick], [#alias] start with '{' returns empty string
+ and list unchanged. *)
let get_name l =
match l with
| "#nick" :: _ | "#alias" :: _ -> "", l
@@ -411,6 +481,7 @@ let get_name l =
end
| _ -> "", l
+(** Parses person's public name if present *)
let get_pub_name l =
match l with
| x :: l' ->
@@ -419,28 +490,33 @@ let get_pub_name l =
else "", l
| _ -> "", l
+(** Parses person's image path if present *)
let get_image l =
match l with
| ("#image" | "#photo") :: x :: l' ->
if !no_picture then "", l' else cut_space x, l'
| _ -> "", l
+(** Parses person's occupation if present *)
let get_occu l =
match l with
| "#occu" :: x :: l' -> cut_space x, l'
| _ -> "", l
+(** Parses person's source if present *)
let get_sources l =
match l with
"#src" :: x :: l' -> cut_space x, l'
| _ -> "", l
+(** Parses person's acces rights *)
let get_access l =
match l with
"#apubl" :: l' -> Public, l'
| "#apriv" :: l' -> Private, l'
| _ -> IfTitles, l
+(** Create [gen_title] from string *)
let scan_title t =
let next_field i =
let rec loop s i =
@@ -485,6 +561,7 @@ let scan_title t =
t_date_start = Adef.cdate_of_od date_start;
t_date_end = Adef.cdate_of_od date_end; t_nth = nth}
+(** Parses list of titles ([gen_title]) if they are present. *)
let rec get_titles str l =
match l with
x :: l' ->
@@ -496,6 +573,7 @@ let rec get_titles str l =
else [], l
| _ -> [], l
+(** Parses person's event name *)
let get_pevent_name str l =
match l with
"#birt" :: l' -> Epers_Birth, l'
@@ -554,6 +632,7 @@ let get_pevent_name str l =
else failwith str
| _ -> failwith str
+(** Parses family event name *)
let get_fevent_name str l =
match l with
| "#marr" :: l' -> Efam_Marriage, l'
@@ -573,6 +652,7 @@ let get_fevent_name str l =
else failwith str
| _ -> failwith str
+(** Parses event date if it is present. *)
let get_optional_event_date l =
match l with
x :: l' ->
@@ -586,12 +666,21 @@ let get_optional_event_date l =
end
| _ -> None, l
+(** Parse witness kind *)
let get_event_witness_kind l =
match l with
"#godp" :: l' -> Witness_GodParent, l'
| "#offi" :: l' -> Witness_Officer, l'
| _ -> Witness, l
+(** Parses the line containing an information about relationship between parents within family
+ and returns [((relk, fath_sex, moth_sex), mar, place, note, src, divorce, rest)].
+ [relk] i a relation kind between parents ([Def.relation_kind]), [fath_sex] and [moth_sex]
+ is a sex of each parent, [mar] is a optional mariage date (if married), [place] is a
+ marriage place if present, [note] is a mariage note if present, [src] is a mariage source
+ if present, [divorce] is a divorce status [Def.divorce], [rest] is the rest of the line to
+ parse
+*)
let get_mar_date str =
function
x :: l ->
@@ -657,10 +746,13 @@ let get_mar_date str =
relation, mar, place, note, src, divorce, l
| [] -> failwith str
+(** Read and return a line with list of words that appears on this line. If
+ reading raises [Enf_of_file] returns [None] *)
let read_line ic =
try let str = input_real_line ic in Some (str, fields str) with
End_of_file -> None
+(** Create a dummy [gen_person]. *)
let create_person () =
{first_name = ""; surname = ""; occ = 0; image = ""; public_name = "";
qualifiers = []; aliases = []; first_names_aliases = [];
@@ -673,8 +765,18 @@ let create_person () =
burial_note = ""; burial_src = ""; pevents = []; notes = ""; psources = "";
key_index = Gwdb.dummy_iper}
+(** Person is unknown (bogus definition) *)
let bogus_def p n = p = "?" || n = "?"
+(** Parse the line and create person's [gen_person] definition.
+ Doesn't modify folowing personal information:
+ - Key
+ - Parents
+ - Related persons
+ - Events
+ - Notes
+ If can't parse person's sources use [comm_psources] instead.
+ If can't parse bithdate use [comm_birth_place] instead. *)
let set_infos fn sn occ sex comm_psources comm_birth_place str u l =
let (first_names_aliases, l) = get_fst_names_aliases str l in
let (surnames_aliases, l) = get_surnames_aliases str l in
@@ -693,6 +795,7 @@ let set_infos fn sn occ sex comm_psources comm_birth_place str u l =
let (baptism, l) = get_optional_baptdate l in
let (baptism_place, l) =
let (pp, l) = get_field "#pp" l in
+ (* if no baptization place then it's equals to birth place *)
if pp = "" then get_field "#bp" l else pp, l
in
let (bapt_note, l) = get_field "#pn" l in
@@ -744,9 +847,16 @@ let set_infos fn sn occ sex comm_psources comm_birth_place str u l =
in
u, l
+(** Parses the line containing a parent and returns [(somebody,np,rest)]. [somebody] is either [Defined p] if
+ person's definiton was parsed ([p] regroups all personal information) either [Undefined k] if a reference
+ to a person already defined was parsed ([k] is a key to find corresponding definition). [np] is a person's
+ surname. [rest] is a rest of line to parse. Could be used to parse familial witnesses. *)
let parse_parent str l =
+ (* last name *)
let (np, l) = get_name l in
+ (* first name and occurence number *)
let (pp, op, l) = get_fst_name str l in
+ (* person is not defined as a child elsewhere (is defined here) *)
let defined =
if bogus_def pp np then true
else
@@ -762,6 +872,10 @@ let parse_parent str l =
let u = create_person () in
let (u, l) = set_infos pp np op u.sex "" "" str u l in Defined u, np, l
+(** Parses the line containing a childran and returns a person [gen_person] containing
+ all extracted information. If a childran definition doesn't provide
+ surname then father's surname is used. ALso if it doesn't provide a childran's
+ birth place and source then it uses information provided by family definiton. *)
let parse_child str surname sex csrc cbp l =
let u = create_person () in
let (prenom, occ, l) = get_fst_name str l in
@@ -779,6 +893,8 @@ let parse_child str surname sex csrc cbp l =
in
set_infos prenom nom occ sex csrc cbp str u l
+(** Parse relation type [Def.gen_relation] with a person outside of family block
+ (foster parents, god parent, etc.). *)
let get_relation str =
function
"-" :: x :: l ->
@@ -815,6 +931,8 @@ let get_relation str =
end
| _ -> failwith str
+(** Read notes of a person inside [note] block across multiple lines and
+ concat them. *)
let read_notes ic =
let notes =
try
@@ -829,6 +947,8 @@ let read_notes ic =
Mutil.strip_all_trailing_spaces notes
(* from version 5.00 *)
+(** Read database notes across multiple lines and concat them. Stop reading when
+ encounter [end_text] *)
let read_notes_db ic end_txt =
let notes =
try
@@ -848,13 +968,20 @@ let read_notes_db ic end_txt =
in
Mutil.strip_all_trailing_spaces notes
+(** Parsing status of .gw block *)
type 'a read_family =
- F_some of 'a
+ (** Read block inside .gw file *)
+ | F_some of 'a
+ (** Read block that defines that file use utf-8 encoding *)
| F_enc_utf_8
+ (** Read block that defines that the file uses gwplus syntax *)
| F_gw_plus
+ (** Read end of the file *)
| F_none
+ (** Exception while reading *)
| F_fail of string
+(** Read succesive family note lines and concat it. *)
let loop_note line ic =
let rec loop_note acc str =
match fields str with
@@ -872,6 +999,9 @@ let loop_note line ic =
in
loop_note [] line
+(** Parse witnesses across the lines and returns list of [(wit,wsex,wk)]
+ where wit is a witness definition/reference, [wsex] is a sex of witness
+ and [wk] is a kind of witness relationship to the family. *)
let loop_witn line ic =
let rec loop_witn acc str =
match fields str with
@@ -890,20 +1020,29 @@ let loop_witn line ic =
in
loop_witn [] line
+(** Read and parse a gw file block from [ic]. Returns also next line if it's
+ not the end of the file. *)
let read_family ic fname =
function
- Some (_, ["encoding:"; "utf-8"]) -> F_enc_utf_8
+ (* Block that defines that file use utf-8 encoding *)
+ | Some (_, ["encoding:"; "utf-8"]) -> F_enc_utf_8
+ (* Block that defines that the file uses gwplus syntax *)
| Some (_, ["gwplus"]) -> F_gw_plus
+ (* Family block *)
| Some (str, "fam" :: l) ->
+ (* read father *)
let (fath_key, surname, l) = parse_parent str l in
+ (* read relation between parents *)
let (relation_ss, marriage, marr_place, marr_note, marr_src, divorce,
l) =
get_mar_date str l
in
let (relation, fath_sex, moth_sex) = relation_ss in
+ (* read mother *)
let (moth_key, _, l) = parse_parent str l in
if l <> [] then failwith str;
let line = read_line ic in
+ (* read list of witnesses with their sex (if exists) *)
let (witn, line) =
let rec loop =
function
@@ -922,25 +1061,30 @@ let read_family ic fname =
in
loop line
in
+ (* read familial source if present *)
let (fsrc, line) =
match line with
Some (_, ["src"; x]) -> cut_space x, read_line ic
| Some (str, "src" :: _) -> failwith str
| _ -> "", line
in
+ (* read common children source if present *)
let (csrc, line) =
match line with
Some (_, ["csrc"; x]) -> cut_space x, read_line ic
| Some (str, "csrc" :: _) -> failwith str
| _ -> "", line
in
+ (* read common children birth place if present *)
let (cbp, line) =
match line with
Some (_, ["cbp"; x]) -> cut_space x, read_line ic
| Some (str, "cbp" :: _) -> failwith str
| _ -> "", line
in
+ (* create a couple *)
let co = Adef.couple fath_key moth_key in
+ (* read a family comments *)
let (comm, line) =
match line with
Some (str, "comm" :: _) ->
@@ -948,6 +1092,7 @@ let read_family ic fname =
comm, read_line ic
| _ -> "", line
in
+ (* read family events *)
let (fevents, line) =
match line with
Some (_, "fevt" :: _) ->
@@ -983,7 +1128,8 @@ let read_family ic fname =
| _ -> [], line
in
begin match line with
- Some (_, ["beg"]) ->
+ (* have children *)
+ | Some (_, ["beg"]) ->
let cles_enfants =
let rec loop children =
match read_line ic with
@@ -997,6 +1143,7 @@ let read_family ic fname =
in
List.rev (loop [])
in
+ (* create a family definition (without witnesses, events and family index) *)
let fo =
{marriage = marriage; marriage_place = marr_place;
marriage_note = marr_note; marriage_src = marr_src;
@@ -1008,6 +1155,7 @@ let read_family ic fname =
F_some
(Family (co, fath_sex, moth_sex, witn, fevents, fo, deo),
read_line ic)
+ (* no children *)
| line ->
let fo =
{marriage = marriage; marriage_place = marr_place;
@@ -1020,9 +1168,11 @@ let read_family ic fname =
F_some
(Family (co, fath_sex, moth_sex, witn, fevents, fo, deo), line)
end
+ (* Database notes block *)
| Some (_, ["notes-db"]) ->
let notes = read_notes_db ic "end notes-db" in
F_some (Bnotes ("", notes), read_line ic)
+ (* Extended page block *)
| Some (str, ["page-ext"; _]) ->
let p =
let len = String.length "page-ext" + 1 in
@@ -1030,9 +1180,10 @@ let read_family ic fname =
in
let notes = read_notes_db ic "end page-ext" in
F_some (Bnotes (p, notes), read_line ic)
+ (* Used before version 5.00. Notes block *)
| Some (_, ["notes"]) ->
- (* used before version 5.00 *)
let notes = read_notes ic in F_some (Bnotes ("", notes), read_line ic)
+ (* Notes block *)
| Some (str, "notes" :: l) ->
let (surname, l) = get_name l in
let (first_name, occ, l) = get_fst_name str l in
@@ -1048,6 +1199,7 @@ let read_family ic fname =
| Some (str, _) -> failwith str
| None -> failwith "end of file"
end
+ (* Wizard note block *)
| Some (str, "wizard-note" :: _) ->
let wizid =
let len = String.length "wizard-note " in
@@ -1055,8 +1207,11 @@ let read_family ic fname =
in
let notes = read_notes_db ic "end wizard-note" in
F_some (Wnotes (wizid, notes), read_line ic)
+ (* Personal relation block *)
| Some (str, "rel" :: l) ->
+ (* get considered person *)
let (sb, _, l) = parse_parent str l in
+
let (sex, l) =
match l with
"#h" :: l -> Male, l
@@ -1066,7 +1221,8 @@ let read_family ic fname =
if l <> [] then failwith "str"
else
begin match read_line ic with
- Some (_, ["beg"]) ->
+ (* Read list of relations *)
+ | Some (_, ["beg"]) ->
let rl =
try
let rec loop =
@@ -1081,7 +1237,9 @@ let read_family ic fname =
| Some (str, _) -> failwith str
| None -> failwith "end of file"
end
+ (* Person's events block *)
| Some (str, "pevt" :: l) ->
+ (* get considered person *)
let (sb, _, l) = parse_parent str l in
if l <> [] then failwith str
else
@@ -1116,20 +1274,26 @@ let read_family ic fname =
let pevents = List.rev pevents in
F_some (Pevent (sb, Neuter, pevents), read_line ic)
| Some (str, _) -> failwith str
+ (* End of the file *)
| None -> F_none
+(** Read and return a block of .gw file. If [!no_fail] is disabled raises
+ [Failure] exception. *)
let read_family_1 ic fname line =
if !no_fail then
try read_family ic fname line with Failure str -> F_fail str
else read_family ic fname line
+(** Compile .gw file and save result to corresponding .gwo *)
let comp_families x =
let out_file = Filename.chop_suffix x ".gw" ^ ".gwo" in
line_cnt := 0;
let oc = open_out_bin out_file in
begin try
let ic = open_in x in
+ (* write header *)
output_string oc magic_gwo;
+ (* write source filename *)
output_value oc (x : string);
let rec loop line encoding =
match read_family_1 (ic, encoding) x line with
diff --git a/bin/gwc/gwcomp.mli b/bin/gwc/gwcomp.mli
index 6dc1eecf10..e94cf30852 100644
--- a/bin/gwc/gwcomp.mli
+++ b/bin/gwc/gwcomp.mli
@@ -4,35 +4,85 @@ open Geneweb
open Def
open Gwdb
+(** Key to refer a person's definition *)
type key = { pk_first_name : string; pk_surname : string; pk_occ : int }
+
+(** Represents a person in .gw file. It could be either reference to a person
+ (only key elements provided) or definition (all information provided). *)
type somebody =
- Undefined of key
+ (** Reference to person *)
+ | Undefined of key
+ (** Person's definition *)
| Defined of (iper, iper, string) gen_person
+(** Blocks that could appear in .gw file. *)
type gw_syntax =
- Family of
- somebody gen_couple * sex * sex * (somebody * sex) list *
- (string gen_fam_event_name * cdate * string * string * string *
- string * (somebody * sex * witness_kind) list)
- list *
- ((iper, iper, string) gen_person, ifam, string) gen_family *
- (iper, iper, string) gen_person gen_descend
+ (** Family definition block. Contains:
+ - Family couple (father's and mother's definition/reference)
+ - Father's sex
+ - Mother's sex
+ - List of witnesses definition/reference with their sex.
+ - List of information about every family event (name, date,
+ place, reason, source, notes and witnesses)
+ - Family definition
+ - Children (descendants) *)
+ | Family of
+ somebody gen_couple
+ * sex
+ * sex
+ * (somebody * sex) list
+ * (string gen_fam_event_name * cdate * string * string * string *
+ string * (somebody * sex * witness_kind) list) list
+ * ((iper, iper, string) gen_person, ifam, string) gen_family
+ * (iper, iper, string) gen_person gen_descend
+ (** Block that defines personal notes. First element represents
+ reference to person. Second is note's content. *)
| Notes of key * string
+ (** Block that defines relations of a person with someone outisde of
+ family block. Contains:
+ - Concerned person definition/reference
+ - Sex of person
+ - List of his relations. *)
| Relations of somebody * sex * (somebody, string) gen_relation list
+ (** Block that defines events of a person. Specific to gwplus format. Contains:
+ - Concerned person definition/reference
+ - Sex of person
+ - List of information about every personal event (name, date,
+ place, reason, source, notes and witnesses)*)
| Pevent of
- somebody * sex *
- (string gen_pers_event_name * cdate * string * string * string *
- string * (somebody * sex * witness_kind) list)
- list
+ somebody
+ * sex
+ * (string gen_pers_event_name * cdate * string * string * string *
+ string * (somebody * sex * witness_kind) list) list
+ (** Block that defines database notes and extended pages.
+ First string represents name of extended page ("" for
+ database notes, only one for file). Second is note's
+ or page's content. *)
| Bnotes of string * string
+ (** Block that defines wizard notes. First string represents
+ First string represents wizard's id. Second is note's content. *)
| Wnotes of string * string
+(** .gwo file header *)
val magic_gwo : string
+
+(** Line counter while reading .gw file *)
val line_cnt : int ref
+
+(** Do not raise exception if syntax error occured.
+ Instead print error information on stdout *)
val no_fail : bool ref
-val comp_families : string -> unit
+
+(** Save path to the images *)
val no_picture : bool ref
+
+(** Forces to create all the keys for every persons (even for ? ?).
+ Enabled for gwplus format. *)
val create_all_keys : bool ref
+(** Compile .gw file and save result to corresponding .gwo *)
+val comp_families : string -> unit
+
(* Ajout pour l'API *)
+(** Parses [Def.date] from string that starts at pos [i] inside [s] *)
val date_of_string : string -> int -> date option
diff --git a/bin/gwd/gwd.ml b/bin/gwd/gwd.ml
index 6f5b92d632..257f354d1b 100644
--- a/bin/gwd/gwd.ml
+++ b/bin/gwd/gwd.ml
@@ -659,7 +659,7 @@ let allowed_denied_titles key extra_line env base_env () =
if fname = "" then []
else
let ic =
- Secure.open_in (Filename.concat (Secure.bd ()) fname)
+ Secure.open_in (Filename.concat (Secure.base_dir ()) fname)
in
let rec loop set =
let (line, eof) =
@@ -1930,7 +1930,7 @@ let main () =
images_url := "file://" ^ slashify abs_dir
end;
if !(Util.cnt_dir) = Filename.current_dir_name then
- Util.cnt_dir := Secure.bd ();
+ Util.cnt_dir := Secure.base_dir ();
Wserver.stop_server :=
List.fold_left Filename.concat !(Util.cnt_dir) ["cnt"; "STOP_SERVER"];
let (query, cgi) =
diff --git a/bin/gwd/gwdLog.mli b/bin/gwd/gwdLog.mli
new file mode 100644
index 0000000000..4f47e6af0f
--- /dev/null
+++ b/bin/gwd/gwdLog.mli
@@ -0,0 +1,28 @@
+(** Verbosity level: defines the verbosity level that will
+ allow the `syslog` function to print anything. *)
+val verbosity : int ref
+
+(** If set to `true`, prints backtrace when printng log. *)
+val debug : bool ref
+
+(** The output channel in which log is written. *)
+val oc : out_channel option ref
+
+(** Prints on `oc` *)
+val log : (out_channel -> unit) -> unit
+
+(** The level of log. *)
+type level = [
+ | `LOG_EMERG (** Print if `!verbosity >= 0` *)
+ | `LOG_ALERT (** Print if `!verbosity >= 1` *)
+ | `LOG_CRIT (** Print if `!verbosity >= 2` *)
+ | `LOG_ERR (** Print if `!verbosity >= 3` *)
+ | `LOG_WARNING (** Print if `!verbosity >= 4` *)
+ | `LOG_NOTICE (** Print if `!verbosity >= 5` *)
+ | `LOG_INFO (** Print if `!verbosity >= 6` *)
+ | `LOG_DEBUG (** Print if `!verbosity >= 7` *)
+ ]
+
+(** [syslog level msg]
+ Prints `msg` on `!oc` depending on the verbosity. *)
+val syslog : level -> string -> unit
diff --git a/bin/gwd/gwdPlugin.mli b/bin/gwd/gwdPlugin.mli
new file mode 100644
index 0000000000..5c2d0c6295
--- /dev/null
+++ b/bin/gwd/gwdPlugin.mli
@@ -0,0 +1,15 @@
+val assets : string ref
+
+val ht : (string, (string * (Geneweb.Config.config -> Gwdb.base option -> bool))) Hashtbl.t
+
+val register :
+ ns:string ->
+ (string * (string -> Geneweb.Config.config -> Gwdb.base option -> bool)) list ->
+ unit
+
+val se : (string * (Geneweb.Config.config -> Gwdb.base option -> unit)) list ref
+
+val register_se :
+ ns:string ->
+ (string -> Geneweb.Config.config -> Gwdb.base option -> unit) ->
+ unit
diff --git a/bin/gwd/gwdPluginDep.mli b/bin/gwd/gwdPluginDep.mli
new file mode 100644
index 0000000000..10ecf8bd83
--- /dev/null
+++ b/bin/gwd/gwdPluginDep.mli
@@ -0,0 +1,10 @@
+type 'a sort_result = Sorted of 'a list | ErrorCycle of 'a list
+
+(** Given a list of elements (in this case, plugins) and their dependencies,
+ tries to compute a valid order `l` and return `Sorted l` .
+ If there is a cycle, returns `ErrorCycle l'` where `l'` is a dependency
+ cycle.
+ Uses Kahn's algorithm for cycle detection.
+ *)
+val sort : ('a * 'a list) list -> 'a sort_result
+
diff --git a/bin/gwd/gwdPluginMD5.mli b/bin/gwd/gwdPluginMD5.mli
new file mode 100644
index 0000000000..54fb7b60e4
--- /dev/null
+++ b/bin/gwd/gwdPluginMD5.mli
@@ -0,0 +1 @@
+val allowed : string -> bool
diff --git a/bin/gwd/gwdPluginMETA.mli b/bin/gwd/gwdPluginMETA.mli
new file mode 100644
index 0000000000..d38fc780f1
--- /dev/null
+++ b/bin/gwd/gwdPluginMETA.mli
@@ -0,0 +1,7 @@
+type meta =
+ { version : string
+ ; maintainers : string list
+ ; depends : string list
+ }
+
+val parse : string -> meta
diff --git a/bin/gwd/request.ml b/bin/gwd/request.ml
index 3c2055604d..3b5c63dce6 100644
--- a/bin/gwd/request.ml
+++ b/bin/gwd/request.ml
@@ -211,6 +211,7 @@ let very_unknown conf _ =
Hutil.trailer conf
| None -> incorrect_request conf
+(* Print Not found page *)
let unknown = begin fun conf n ->
let title _ =
Output.printf conf "%s: \"%s\"" (Utf8.capitalize_fst (transl conf "not found"))
diff --git a/bin/gwd/request.mli b/bin/gwd/request.mli
index a93f050550..98a0d43b21 100644
--- a/bin/gwd/request.mli
+++ b/bin/gwd/request.mli
@@ -48,7 +48,6 @@ val w_person
(**/**)
(* Used internally by [gwd]. Not intended to be used by other programs. *)
-val special_vars : string list
val treat_request : Config.config -> unit
(**/**)
diff --git a/bin/gwd/robot.mli b/bin/gwd/robot.mli
new file mode 100644
index 0000000000..14b13d72da
--- /dev/null
+++ b/bin/gwd/robot.mli
@@ -0,0 +1,52 @@
+(** A module handling robots requests *)
+(* S: This module seems obsolete *)
+
+val magic_robot : string
+
+module W : Map.S with type key = string
+
+type norfriwiz = Normal | Friend of string | Wizard of string
+
+type who = private {
+ acc_times : float list; (** The timings of the connexion attempts *)
+ oldest_time : float; (** The first connection in the specified window
+ (check option -robot-xcl) of time in which successive
+ connections are attempted. *)
+ nb_connect : int; (** The number of connection in the specified window. *)
+ nbase : string; (** Always be equal to conf.bname *)
+ utype : norfriwiz; (** The kind of robot *)
+}
+
+(** A collection of robots: the list contains forbidden robots and
+ the map contains accepted (under conditions) robots. *)
+type excl = {
+ mutable excl : (string * int ref) list;
+ mutable who : who W.t;
+ mutable max_conn : int * string;
+}
+
+(** Prints an error "Access refuned" in HTML and raises an `Exit` exception. *)
+val robot_error : Geneweb.Config.config -> int -> int -> 'a
+
+(** Reads the content of the admin file managing robots and returns its content
+ and the full file name. *)
+val robot_excl : unit -> excl * string
+
+val min_disp_req : int ref
+
+(** [check tm from max_call sec conf suicide]
+ Returns a tuple containing:
+ * the number of robots who attempted to connect twice
+ * the number of wizard robots who attempted to connect twice
+ * the number of friend robots who attempted to connect twice
+ * the wizards list and their last connection attempt.
+ It also updates the robot file by blocking robots who did too many attempts.
+*)
+val check :
+ float ->
+ string ->
+ int ->
+ int ->
+ Geneweb.Config.config ->
+ bool ->
+ int * int * int * (string * float) list
diff --git a/bin/gwdiff/gwdiff.ml b/bin/gwdiff/gwdiff.ml
index edb6ebe916..0fd4ca6c00 100644
--- a/bin/gwdiff/gwdiff.ml
+++ b/bin/gwdiff/gwdiff.ml
@@ -15,6 +15,8 @@ let html = ref false
let root = ref ""
let cr = ref ""
+(* Messages are printed when there is a difference between a person
+ present in the two bases explored. *)
type messages =
MsgBadChild of iper
| MsgBirthDate
@@ -34,6 +36,9 @@ type messages =
| MsgSpouses of iper
| MsgSurname
+(* [person_string base iper]
+ Returns the string associated to the person with id `iper` in the base
+ `base`. *)
let person_string base iper =
let p = poi base iper in
let fn = sou base (get_first_name p) in
@@ -42,12 +47,15 @@ let person_string base iper =
fn ^ " " ^ sn ^ " (#" ^ string_of_iper iper ^ ")"
else fn ^ "." ^ string_of_int (get_occ p) ^ " " ^ sn
+(* Returns the string associated to a person in HTML if the html option is set,
+ otherwise it has the same effect tja, `person_string`. *)
let person_link bname base iper target =
if !html then
Printf.sprintf "%s" !root bname
(string_of_iper iper) target (person_string base iper)
else person_string base iper
+(* Prints a message *)
let print_message base1 msg =
Printf.printf " ";
begin match msg with
@@ -81,6 +89,7 @@ let print_message base1 msg =
end;
Printf.printf "%s" !cr
+(* Prints messages associates to the two families identifiers in argument *)
let print_f_messages base1 base2 ifam1 ifam2 res =
let f1 = foi base1 ifam1 in
let f2 = foi base2 ifam2 in
@@ -91,19 +100,30 @@ let print_f_messages base1 base2 ifam1 ifam2 res =
(person_link !in_file2 base2 (get_father f2) "base2") !cr;
List.iter (print_message base1) res
+(* Same, but for persons *)
let print_p_messages base1 base2 iper1 iper2 res =
Printf.printf "%s / %s%s" (person_link !in_file1 base1 iper1 "base1")
(person_link !in_file2 base2 iper2 "base2") !cr;
List.iter (print_message base1) res
+(* [compatible_names src_name dest_name_list]
+ Returns true if `src_name` is in `dest_name_list` (case insensitive) *)
let compatible_names src_name dest_name_list =
let src_name = Name.lower src_name in
let dest_name_list = List.map Name.lower dest_name_list in
List.mem src_name dest_name_list
+(* [compatible_str_field istr1 istr2]
+ Checks the compatibility of two string identifiers, i.e.
+ if istr1 is not the empty string identifier, then istr2
+ must not be. *)
let compatible_str_field istr1 istr2 =
is_empty_string istr1 || not (is_empty_string istr2)
+(* Returns a list of intervals of SDN (SDN 1 is November 25, 4714 BC Gregorian
+ calendar) of the date in argument. An interval has the format (b, b'),
+ where b is an optional lower bound (None => no bound), and b' an optional
+ upper bound. *)
let dmy_to_sdn_range_l dmy =
let sdn_of_dmy dmy =
let sdn = Calendar.sdn_of_gregorian dmy in
@@ -130,6 +150,7 @@ let dmy_to_sdn_range_l dmy =
in
sdn, sdn2
in
+ (* S: calls to sdn_of_dmy dmy can be factorized *)
match dmy.prec with
Sure -> let (sdn1, sdn2) = sdn_of_dmy dmy in [Some sdn1, Some sdn2]
| Maybe ->
@@ -149,9 +170,13 @@ let dmy_to_sdn_range_l dmy =
let (_sdn21, sdn22) = sdn_of_dmy (Date.dmy_of_dmy2 dmy2) in
[Some sdn11, Some sdn22]
+(* [compatible_sdn i1 i2]
+ Checks if two intervals `i1` and `i2` (as described for `dmy_to_sdn_range_l`)
+ are compatible, i.e. if i2 is a sub interval of i1. *)
let compatible_sdn (sdn11, sdn12) (sdn21, sdn22) =
if (sdn21, sdn22) = (None, None) then true
else
+ (* S: Add unit argument to bool2 to make good use of OCaml laziness *)
let bool1 =
match sdn11, sdn21 with
Some sdn1, Some sdn2 -> sdn1 <= sdn2
@@ -166,15 +191,27 @@ let compatible_sdn (sdn11, sdn12) (sdn21, sdn22) =
in
bool1 && bool2
+(* [compatible_sdn_l l i]
+ Checks if there exists an interval in `l` that is compatible with `i` *)
let compatible_sdn_l sdn1_l sdn2 =
+ (* S: replace by List.exists *)
List.fold_left (fun r sdn1 -> r || compatible_sdn sdn1 sdn2) false sdn1_l
+(* [compatible_sdn_l l1 l2]
+ Checks if for all intervals `i2` in `l2`, there exists an interval `i1` in
+ `l1` such that `i1` is compatible with `i2` *)
let compatible_sdn_ll sdn1_l sdn2_l =
List.fold_left (fun r sdn2 -> r && compatible_sdn_l sdn1_l sdn2) true sdn2_l
+(* [compatible_dmys d1 d2]
+ Checks if `d1` is compatible with `d2`, i.e. if despite a potential lack
+ of precision in the dates, d2 is more precise than d1. *)
let compatible_dmys dmy1 dmy2 =
compatible_sdn_ll (dmy_to_sdn_range_l dmy1) (dmy_to_sdn_range_l dmy2)
+(* [compatible_dates date1 date2]
+ Same than before, but also checks the kind of date (Dgreg or Dtext)
+ and, in the first case, if calendars are compatible. *)
let compatible_dates date1 date2 =
let compatible_cals cal1 cal2 =
match cal1, cal2 with
@@ -189,6 +226,7 @@ let compatible_dates date1 date2 =
| Dgreg (_, _), Dtext _ -> false
| Dtext _, _ -> true
+(* Same than before, but for Adef.ctype. *)
let compatible_cdates cdate1 cdate2 =
let od1 = Adef.od_of_cdate cdate1 in
let od2 = Adef.od_of_cdate cdate2 in
@@ -197,6 +235,12 @@ let compatible_cdates cdate1 cdate2 =
| Some _, None -> false
| None, _ -> true
+(* Checks if birth between two persons are compatible, i.e. if their birth date
+ (baptism date if birth date not provided) and place are compatible, and
+ returns a list of messages.
+ If birth is not provided, checks bathism date instead.
+ If birth/bathism date are not compatible, the returned list will have MsgBirthDate
+ If birth place are not compatible, the returned list will have MsgBirthPlace *)
let compatible_birth p1 p2 =
let get_birth person =
if person.birth = Adef.cdate_None then person.baptism else person.birth
@@ -212,6 +256,8 @@ let compatible_birth p1 p2 =
in
res1 @ res2
+(* Same than before, but for death. Messages returned are
+ MsgDeathDate and MsgDeathPlace *)
let compatible_death p1 p2 =
let bool1 =
p1.death = p2.death ||
@@ -223,7 +269,7 @@ let compatible_death p1 p2 =
DeadDontKnowWhen, (Death (_, _) | DeadYoung | DeadDontKnowWhen) |
DontKnowIfDead, _ ->
true
- | _ -> false)
+ | _ -> (* S: avoid non-exhaustive pattern matching *) false)
in
let res1 = if bool1 then [] else [MsgDeathDate] in
let res2 =
@@ -232,13 +278,21 @@ let compatible_death p1 p2 =
in
res1 @ res2
+(* [compatible_sexes p1 p2]
+ Returns [] if `p1` and `p2` have the same sex, [MsgSex] otherwise. *)
let compatible_sexes p1 p2 =
if p1.sex = p2.sex then [] else [MsgSex]
+(* [compatible_occupations p1 p2]
+ Returns [] if `p1` and `p2` have compatible occupations, [MsgOccupation] otherwise. *)
let compatible_occupations p1 p2 =
if compatible_str_field p1.occupation p2.occupation then []
else [MsgOccupation]
+(* Checks if two persons' names are compatible wrt. their eventual aliases and returns a
+ list of messages.
+ If first names are not compatible, the returned list will have MsgFirstName.
+ If surnames are not compatible, the returned list will have MsgSurname. *)
let compatible_persons_ligth base1 base2 p1 p2 =
let fn1 = sou base1 p1.first_name in
let fn2 = sou base2 p2.first_name in
@@ -250,13 +304,19 @@ let compatible_persons_ligth base1 base2 p1 p2 =
let res2 = if compatible_names sn1 asn2 then [] else [MsgSurname] in
res1 @ res2
+(* Checks if two persons are compatible and returns all the messages associated
+ to the compatiblity of their name, sex, birth, death and occupation. *)
let compatible_persons base1 base2 p1 p2 =
compatible_persons_ligth base1 base2 p1 p2 @
compatible_sexes p1 p2 @ compatible_birth p1 p2 @
compatible_death p1 p2 @
compatible_occupations p1 p2
+(* [find_compatible_persons_ligth base1 base2 iper1 iper2_list]
+ Returns the sublist of persons of `iper2_list` that are compatible with
+ `iper1` (only checking names). *)
let rec find_compatible_persons_ligth base1 base2 iper1 iper2_list =
+ (* S: not tail recursive, could be *)
match iper2_list with
[] -> []
| head :: rest ->
@@ -266,6 +326,8 @@ let rec find_compatible_persons_ligth base1 base2 iper1 iper2_list =
if compatible_persons_ligth base1 base2 p1 p2 = [] then head :: c_rest
else c_rest
+(* Same than before, but with full compatibility ( name, sex, birth, death and
+ occupation) *)
let rec find_compatible_persons base1 base2 iper1 iper2_list =
match iper2_list with
[] -> []
@@ -276,6 +338,8 @@ let rec find_compatible_persons base1 base2 iper1 iper2_list =
if compatible_persons base1 base2 p1 p2 = [] then head :: c_rest
else c_rest
+(* Checks if the spouse of the persons (whose id are in argument) are
+ compatible (only checking names) and returns the associated messages list. *)
let compatible_unions base1 base2 iper1 iper2 ifam1 ifam2 =
let get_spouse base iper ifam =
let f = foi base ifam in
@@ -286,6 +350,9 @@ let compatible_unions base1 base2 iper1 iper2 ifam1 ifam2 =
let spouse2 = gen_person_of_person (get_spouse base2 iper2 ifam2) in
compatible_persons_ligth base1 base2 spouse1 spouse2
+(* [find_compatible_unions base1 base2 iper1 iper2_list ifam1 ifam2_list]
+ Returns the sublist of families of `ifam2_list` whose union is compatible
+ (in the sense of `compatible_unions`). *)
let rec find_compatible_unions base1 base2 iper1 iper2 ifam1 ifam2_list =
match ifam2_list with
[] -> []
@@ -297,12 +364,18 @@ let rec find_compatible_unions base1 base2 iper1 iper2 ifam1 ifam2_list =
head :: c_rest
else c_rest
+(* [compatible_divorces d1 d2]
+ Returns true if divorces are compatible, i.e. if both divorced, then
+ checking date compatibility, if d1 is a divorce and d2 is not returns
+ false, otherwise returns true. *)
let compatible_divorces d1 d2 =
match d1, d2 with
Divorced cdate1, Divorced cdate2 -> compatible_cdates cdate1 cdate2
| Divorced _, _ -> false
| _ -> true
+(* Checks the compatibility of marriages (mariage date, divorce
+ and mariage place), then print the list of messages calculated. *)
let compatible_marriages base1 base2 ifam1 ifam2 =
let f1 = gen_family_of_family (foi base1 ifam1) in
let f2 = gen_family_of_family (foi base2 ifam2) in
@@ -320,12 +393,16 @@ let compatible_marriages base1 base2 ifam1 ifam2 =
let res = res1 @ res2 @ res3 in
if res = [] then () else print_f_messages base1 base2 ifam1 ifam2 res
+(* Calculates the compatibility of two persons and prints the associated
+ messages *)
let pdiff base1 base2 iper1 iper2 =
let p1 = gen_person_of_person (poi base1 iper1) in
let p2 = gen_person_of_person (poi base2 iper2) in
let res = compatible_persons base1 base2 p1 p2 in
if res = [] then () else print_p_messages base1 base2 iper1 iper2 res
+(* Calculates the compatibility of two persons' families and prints the
+ associated messages. *)
let compatible_parents base1 base2 iper1 iper2 =
let a1 = get_parents (poi base1 iper1) in
let a2 = get_parents (poi base2 iper2) in
@@ -340,7 +417,11 @@ let compatible_parents base1 base2 iper1 iper2 =
| Some _, None ->
print_p_messages base1 base2 iper1 iper2 [MsgParentsMissing]
+(* Checks che compatibility of two persons and their families, and prints it.
+ This is performed recursively through their descendants *)
let rec ddiff base1 base2 iper1 iper2 d_tab =
+ (* S: Simplify with statement:
+ let ddiff iper1 iper2 = ddiff base1 base2 iper1 iper2 d_tab *)
let d_check = Gwdb.Marker.get d_tab iper1 in
if List.mem iper2 d_check then ()
else
@@ -385,6 +466,7 @@ let rec ddiff base1 base2 iper1 iper2 d_tab =
let u2 = Array.to_list (get_family (poi base2 iper2)) in
pdiff base1 base2 iper1 iper2; List.iter (fu base1 base2 u2) u1
+(* Returns the eldest persons on the base starting from the persons in argument. *)
let rec find_top base1 base2 iper1 iper2 =
let p1 = gen_person_of_person (poi base1 iper1) in
let p2 = gen_person_of_person (poi base2 iper2) in
@@ -411,6 +493,7 @@ let rec find_top base1 base2 iper1 iper2 =
[]
end
+(* Same than ddiff, but starting from the eldest ancestors from the persons in argument *)
let addiff base1 base2 iper1 iper2 d_tab =
let topdiff (iper1, iper2) =
Printf.printf "==> %s / %s%s" (person_link !in_file1 base1 iper1 "base1")
diff --git a/bin/gwexport/gwexport.ml b/bin/gwexport/gwexport.ml
index d516de149b..de1d0b7d26 100644
--- a/bin/gwexport/gwexport.ml
+++ b/bin/gwexport/gwexport.ml
@@ -106,7 +106,12 @@ let speclist c =
module IPS = Util.IperSet
module IFS = Util.IfamSet
-(**/**)
+(** [is_censored_person max_year person_name]
+ Returns [true] iff the person has a birth date that is after max_year and
+ its visibility is not public
+*)
+(* S: Does it mean private persons whose birth year is before 'max_year'
+ are uncensored? *)
let is_censored_person threshold p =
match Adef.od_of_cdate (get_birth p) with
| None -> false
@@ -115,15 +120,32 @@ let is_censored_person threshold p =
| Adef.Dgreg (dmy, _) -> dmy.Adef.year >= threshold && get_access p != Def.Public
| _ -> false
+(** [is_censored_couple base max_year family]
+ Returns [true] if either the father or the mother of a given family in the
+ base is censored
+*)
let is_censored_couple base threshold cpl =
is_censored_person threshold @@ poi base (get_father cpl)
|| is_censored_person threshold @@ poi base (get_mother cpl)
+
+(* The following functions are utils set people as "censored" by marking them.
+ Censoring a person consists in setting a mark defined as:
+ `Marker.get pmark p lor flag`
+
+ This gets the current mark, being 0 or 1, and `lor`s it with `flag` which is `1`.
+ TODO: replace integer markers by booleans
+*)
+
+(** Marks a censored person *)
let censor_person base pmark flag threshold p no_check =
let ps = poi base p in
if no_check || is_censored_person threshold ps
then Marker.set pmark p (Marker.get pmark p lor flag)
+(** Marks all the members of a family that are censored.
+ If a couple is censored, its parents and all its descendants are marked.
+*)
let rec censor_family base pmark fmark flag threshold i no_check =
let censor_unions p =
let uni = poi base p in
@@ -148,6 +170,7 @@ let rec censor_family base pmark fmark flag threshold i no_check =
let censor_spouse iper =
if all_families_censored iper
then Marker.set pmark iper (Marker.get pmark iper lor flag)
+ (* S: Replace this line by `censor_person`? *)
in
if Marker.get fmark i = 0 then
let fam = foi base i in
@@ -158,6 +181,7 @@ let rec censor_family base pmark fmark flag threshold i no_check =
censor_descendants i
end
+(** Marks all the families that are censored in the given base. *)
let censor_base base pmark fmark flag threshold =
Collection.iter begin fun i ->
censor_family base pmark fmark flag threshold i false
@@ -166,27 +190,34 @@ let censor_base base pmark fmark flag threshold =
censor_person base pmark flag threshold i false
end (ipers base)
+(** Set non visible persons and families as censored *)
let restrict_base base per_tab fam_tab flag =
+ (* Starts by censoring non visible persons of the base *)
Collection.iter begin fun i ->
if base_visible_get base (fun _ -> false) i
- then Marker.set per_tab i (Marker.get per_tab i lor flag)
+ then Marker.set per_tab i (Marker.get per_tab i lor flag) (* S: replace by `censor_person` ? *)
end (ipers base) ;
+
Collection.iter begin fun i ->
let fam = foi base i in
- let des_visible =
+ let des_visible = (* There exists a children of the family that is not censored *)
(* FIXME: replace with exists *)
Array.fold_left
(fun check iper -> check || Marker.get per_tab iper = 0) false
(get_children fam)
in
- let cpl_not_visible =
+ let cpl_not_visible = (* Father or mother is censored *)
Marker.get per_tab (get_father fam) <> 0
|| Marker.get per_tab (get_mother fam) <> 0
in
+ (* If all the children, father and mother are censored, then censor family *)
if not des_visible && cpl_not_visible
then Marker.set fam_tab i (Marker.get fam_tab i lor flag)
end (ifams base)
+(** [select_asc conf base max_gen ips]
+ Returns all the ancestors of persons in the list `ips` up to the `max_gen`
+ generation. *)
let select_asc conf base max_gen ips =
let rec loop_asc (gen : int) set ip =
if not @@ IPS.mem ip set then begin
@@ -203,6 +234,12 @@ let select_asc conf base max_gen ips =
in
List.fold_left (loop_asc 0) IPS.empty ips
+(** [select_surname nase pmark fmark surname]
+ Sets a `true` marker to families whose mother or father that
+ match the given surname. Propagates the mark to children
+ that have this surname.
+*)
+(* S: only used by `select_surnames` in a List.iter *)
(* Should it use search engine functions? *)
let select_surname base pmark fmark surname =
let surname = Name.strip_lower surname in
@@ -226,6 +263,12 @@ let select_surname base pmark fmark surname =
end
end (ifams base)
+(** [select_surnames base surnames]
+ Calls `select_surname` on every family that have the given surnames.
+ Returns two functions:
+ * the first takes a person and returns `true` iff it has been selected
+ * the second takes a family and returns `false` iff it has been selected
+*)
let select_surnames base surnames : (iper -> bool) * (ifam -> bool) =
let pmark = Gwdb.iper_marker (Gwdb.ipers base) false in
let fmark = Gwdb.ifam_marker (Gwdb.ifams base) false in
@@ -234,11 +277,14 @@ let select_surnames base surnames : (iper -> bool) * (ifam -> bool) =
, (fun i -> Gwdb.Marker.get fmark i) )
(**/**)
+(** [select_parentship base ip1 ip2]
+ Returns the set of common descendants of ip1 and the
+ ancestors of ip2 and the set of their families. *)
let select_parentship base ip1 ip2 =
let conf = Config.{ empty with wizard = true ; bname = Gwdb.bname base } in
let asc = select_asc conf base max_int [ ip1 ] in
let desc = Util.select_desc conf base (-max_int) [ ip2, 0 ] in
- let ipers =
+ let ipers = (* S: The intersection of asc and desc *)
if IPS.cardinal asc > Hashtbl.length desc
then
Hashtbl.fold
@@ -249,11 +295,12 @@ let select_parentship base ip1 ip2 =
(fun k acc -> if Hashtbl.mem desc k then IPS.add k acc else acc)
asc IPS.empty
in
- let ifams =
+ let ifams = (* S: families *)
IPS.fold begin fun iper acc ->
Array.fold_left begin fun acc ifam ->
- if IFS.mem ifam acc
- || not (IPS.mem (Gutil.spouse iper @@ foi base ifam) ipers)
+ if IFS.mem ifam acc (* S: useless test? *)
+ || not (IPS.mem (Gutil.spouse iper @@ foi base ifam) ipers) (* S: is the partner of the
+ person not in ipers? *)
then acc
else IFS.add ifam acc
end acc (get_family (poi base iper))
@@ -261,7 +308,12 @@ let select_parentship base ip1 ip2 =
in
ipers, ifams
-let select_from_set ipers ifams =
+(** [select_from_set ipers ifams]
+ Returns two functions :
+ * the first returns true if its input is in ipers
+ * the second returns true if its input is in ifams
+*)
+let select_from_set (ipers : IPS.t) (ifams : IFS.t) =
let sel_per i = IPS.mem i ipers in
let sel_fam i = IFS.mem i ifams in
(sel_per, sel_fam)
@@ -294,6 +346,8 @@ let select opts ips =
in
let conf = Config.{ empty with wizard = true } in
let sel_per, sel_fam =
+ (* S: a lot of redundant tests are done here, would be simpler with
+ pattern matchings and factorization. *)
if opts.ascdesc <> None || opts.desc <> None then begin
assert (opts.censor = 0) ;
let asc =
@@ -334,6 +388,7 @@ let select opts ips =
let sel_fam i = IFS.mem i ifams in
(sel_per, sel_fam)
end else match opts.asc with
+ (* opts.ascdesc = None && opts.desc = None *)
| Some asc ->
let ipers = select_asc conf base asc ips in
let per_sel i = IPS.mem i ipers in
diff --git a/bin/gwexport/gwexport.mli b/bin/gwexport/gwexport.mli
new file mode 100644
index 0000000000..40c319b850
--- /dev/null
+++ b/bin/gwexport/gwexport.mli
@@ -0,0 +1,54 @@
+type gwexport_charset = Ansel | Ansi | Ascii | Utf8
+
+type gwexport_opts = {
+ asc : int option; (* Maximum generation of the root's ascendants *)
+ ascdesc : int option; (* Maximum generation of the root's ascendants descendants *)
+ base : (string * Gwdb.base) option; (* The base analyzed *)
+ censor : int; (* Censors the base for 'n' years *)
+ charset : gwexport_charset; (* The charset of the export *)
+ desc : int option; (* Maximum generation of the root's descendants *)
+ img_base_path : string; (* Unused by this module (and not set by options) *)
+ keys : string list; (* Key reference of additional persons to select *)
+ mem : bool; (* Unused by this module *)
+ no_notes : [ `nn | `nnn | `none ]; (* Unused by this module
+ S: Consider simple ADTs *)
+ no_picture : bool; (* Unused by this module *)
+ oc : string * (string -> unit) * (unit -> unit); (* Unused by this module *)
+ parentship : bool; (* If asc, ascdesc and desc are not set & parenting = true, then
+ select individuals involved in parentship between pair of keys
+ (/!\ assumes the input are pairs of keys) *)
+ picture_path : bool; (* Unused by this module *)
+ source : string option; (* Unused by this module *)
+ surnames : string list; (* Used to select persons by their surname *)
+ verbose : bool; (* Unused by this module *)
+}
+
+(** Default set of options *)
+val default_opts : gwexport_opts
+
+(** Given a set of options, returns default command line arguments for selecting
+ elements from a base. The output of this function is the first input of
+ Arg.parse.
+*)
+val speclist : gwexport_opts ref -> (Arg.key * Arg.spec * Arg.doc) list
+(* Used for gwd2ged and gwu. *)
+
+(** [anonfun opts = fun base_name -> ...]
+ Given a set of options `opts` where `!opts.base` is uninitialized,
+ opens the dir `base_name` and initializes !opts.base with the base name.
+ The output of this function is the second argument of Arg.parse.
+*)
+val anonfun : gwexport_opts ref -> Arg.anon_fun
+(* Arg.anon_fun = string -> unit *)
+
+(** Default error message.
+ This is the third argument of Arg.parse. *)
+val errmsg : Arg.usage_msg
+
+(** [select opts ips]
+ Return filters for [iper] and [ifam] to be used when exporting a (portion of a) base.
+*)
+val select :
+ gwexport_opts ->
+ Gwdb.iper list -> (Gwdb.iper -> bool) * (Gwdb.ifam -> bool)
+
diff --git a/bin/gwrepl/data.mli b/bin/gwrepl/data.mli
new file mode 100644
index 0000000000..4bb9336351
--- /dev/null
+++ b/bin/gwrepl/data.mli
@@ -0,0 +1,12 @@
+(* the array of etc/lib/XXX where XXX are either dependencies of geneweb or
+ geneweb/COMPONENT *)
+val directories: string array
+
+(* associations between file names and their (generated) contents: *)
+val cmas : ( string * string ) array (* .cma *)
+val cmis : ( string * string ) array (* .cmi *)
+val shared : ( string * string ) array (* .so *)
+
+(* An md5 of all the names of the files in [cmis] and [cmas] (not
+ their contents). *)
+val md5 : string
diff --git a/bin/gwrepl/mk_data.ml b/bin/gwrepl/mk_data.ml
index df29494b67..36c8c5bc8a 100644
--- a/bin/gwrepl/mk_data.ml
+++ b/bin/gwrepl/mk_data.ml
@@ -1,3 +1,10 @@
+(* This file is used to generate the file 'data.ml', containing all
+ the files (cmis, cmas, .so) that could be used at runtime by
+ a geneweb interpreter.
+
+ See 'data.mli' for the signature of the generated file. *)
+
+
let read_lines p =
let rec loop () = match input_line p with
| exception End_of_file -> close_in p ; []
@@ -30,34 +37,40 @@ let partition_map p l =
let (//) = Filename.concat
let () =
- let ic = open_in ".depend" in
- let lines = read_lines ic in
- close_in ic ;
- let dune_root = List.hd lines in
- let out = List.tl lines in
- let root = dune_root // "_build" // "default" // "lib" in
let opam_swich_prefix = Sys.getenv "OPAM_SWITCH_PREFIX" in
let opam_swich_prefix_lib = opam_swich_prefix // "lib" in
- let aux fn =
- let aux prefix =
- if String.length fn > String.length prefix
- && String.sub fn 0 (String.length prefix) = prefix
- then Some (String.sub fn (String.length prefix) (String.length fn - String.length prefix))
- else None
+
+ let dune_root, root, ( directories0, files0 ) =
+
+ let ic = open_in ".depend" in
+ let lines = read_lines ic in
+ close_in ic ;
+ let dune_root, out = match lines with
+ | [] -> assert false
+ | dune_root :: out -> dune_root, out
in
- match aux opam_swich_prefix_lib with
- | Some x -> Some (`opam x)
- | None -> match aux root with
- | Some x -> Some (`root x)
- | None -> None
- in
- let directories0, files0 =
+ let root = dune_root // "_build" // "default" // "lib" in
+ let aux fn =
+ let aux prefix =
+ if String.length fn > String.length prefix
+ && String.sub fn 0 (String.length prefix) = prefix
+ then Some (String.sub fn (String.length prefix) (String.length fn - String.length prefix))
+ else None
+ in
+ match aux opam_swich_prefix_lib with
+ | Some x -> Some (`opam x)
+ | None -> match aux root with
+ | Some x -> Some (`root x)
+ | None -> None
+ in
+ dune_root, root,
partition_map begin fun s ->
try Scanf.sscanf s {|#directory "%[^"]";;|} (fun s -> match aux s with Some s -> Some (Either.Left s) | _ -> None)
with _ ->
try Scanf.sscanf s {|#load "%[^"]";;|} (fun s -> match aux s with Some s -> Some (Either.Right s) | _ -> None)
with _ -> failwith s
end out
+
in
let directories =
("etc" // "lib" // "ocaml")
@@ -85,7 +98,13 @@ let () =
if Filename.check_suffix (Filename.concat dir s) "cmi"
then (Filename.concat dir s, "etc" // "lib" // "geneweb" // Filename.concat (Filename.basename dir) s) :: cmis
else cmis
- end cmis (Sys.readdir dir)
+ end cmis (
+ try
+ Sys.readdir dir
+ with exn ->
+ Printf.eprintf "Error in Sys.readdir(%S)\n%!" dir;
+ raise exn
+ )
in
(cmas, cmis)
end files0 ([], [])
@@ -122,12 +141,12 @@ let () =
aux "cmas" cmas
end ;
begin
- Printf.fprintf out {|let shared=[||} ;
- if Sys.unix then (* FIXME: what is the windows version? *)
- List.iter begin fun s ->
- Printf.fprintf out {blob|Filename.(concat "etc" (concat "lib" {|%s|})),[%%blob {|%s|}];|blob} s (opam_swich_prefix_lib // s) ;
- end [ "ocaml" // "stublibs" // "dllcamlstr.so" ; "ocaml" // "stublibs" // "dllunix.so"] ;
- Printf.fprintf out {||];;|}
+ Printf.fprintf out {|let shared=[||} ;
+ if Sys.unix then (* FIXME: what is the windows version? *)
+ List.iter begin fun s ->
+ Printf.fprintf out {blob|Filename.(concat "etc" (concat "lib" {|%s|})),[%%blob {|%s|}];|blob} s (opam_swich_prefix_lib // s) ;
+ end [ "ocaml" // "stublibs" // "dllcamlstr.so" ; "ocaml" // "stublibs" // "dllunix.so"] ;
+ Printf.fprintf out {||];;|}
end ;
begin
let b = Buffer.create 1024 in
diff --git a/bin/gwu/gwuLib.ml b/bin/gwu/gwuLib.ml
index 7f992ffff7..e3081459d5 100644
--- a/bin/gwu/gwuLib.ml
+++ b/bin/gwu/gwuLib.ml
@@ -12,6 +12,12 @@ let raw_output = ref false
let sep_limit = ref 21
let separate_list = ref []
+(* Returns true if `old_gw` is `true` and there exist an event associated to a
+ person that:
+ * is either a birth, baptism, death, burial or a cremation and is associated to
+ a note or a witness;
+ * is any other event.
+ Otherwise, returns false *)
let put_events_in_notes base p =
(* Si on est en mode old_gw, on mets tous les évènements *)
(* dans les notes. *)
@@ -33,9 +39,9 @@ let put_events_in_notes base p =
loop (get_pevents p)
else false
-let ht_dup_occ = Hashtbl.create 20001
+let (ht_dup_occ : (Gwdb.iper, int) Hashtbl.t) = Hashtbl.create 20001
-let ht_orig_occ = Hashtbl.create 20001
+let (ht_orig_occ : (string, int list) Hashtbl.t) = Hashtbl.create 20001
let prepare_free_occ ?(select = fun _ -> true) base =
(* Parce qu'on est obligé ... *)
diff --git a/bin/gwu/gwuLib.mli b/bin/gwu/gwuLib.mli
new file mode 100644
index 0000000000..15047572c2
--- /dev/null
+++ b/bin/gwu/gwuLib.mli
@@ -0,0 +1,21 @@
+
+val out_dir : string ref
+val old_gw : bool ref
+val raw_output : bool ref
+val separate_list : string list ref
+val only_file : string ref
+val sep_limit : int ref
+
+(** Initializes the internal hashtables. Person whose identifier is
+ not selected (`select p = false`) are ignored. *)
+val prepare_free_occ : ?select:(Gwdb.iper -> bool) -> Gwdb.base -> unit
+
+(** Prints the `.gw` file. *)
+val gwu :
+ Gwexport.gwexport_opts ->
+ bool ->
+ Gwdb.base ->
+ string ->
+ string ->
+ (string, (string -> unit) * bool ref * (unit -> unit)) Hashtbl.t ->
+ (Gwdb.iper -> bool) * (Gwdb.ifam -> bool) -> unit
diff --git a/bin/wserver/wserver.mli b/bin/wserver/wserver.mli
index 68c6a0711b..3259abe142 100644
--- a/bin/wserver/wserver.mli
+++ b/bin/wserver/wserver.mli
@@ -30,43 +30,56 @@ val f
-> (Unix.sockaddr * string list -> string -> string -> unit)
-> unit
+(** Closes the current socket *)
val close_connection : unit -> unit
+(** Formatter printing in the out channel associated to the connected socket *)
val printf : ('a, out_channel, unit) format -> 'a
(* To be called to print page contents. *)
+(** Prints a string in the out channel associated to the socket *)
val print_string : string -> unit
(* To be called to print page contents. *)
+(** Prints a header; cannot be called if part of content part already has been sent *)
val header : string -> unit
(* To print an http header line *)
+(** Flushes the content of the current socket *)
val wflush : unit -> unit
(* To flush page contents print. *)
+
+(** [Output.status conf answer] sends the http header where [answer]
+ represents the answer status. *)
val http : Def.httpStatus -> unit
- (* [Output.status conf answer] sends the http header where [answer]
- represents the answer status. If empty string, "200 OK" is assumed. *)
+(** [Output.status conf_redirect url] sends the http header where [url]
+ represents the Location where the request needs to be redirected. *)
val http_redirect_temporarily : string -> unit
- (* [Output.status conf_redirect url] sends the http header where [url]
- represents the Location where the request needs to be redirected. *)
+(** Returns the request from a stream read from a socket. *)
val get_request_and_content : char Stream.t -> string list * string
+(** Returns the last used socket *)
val wsocket : unit -> Unix.file_descr
+
+(** Return the out_channel associated to the socket *)
val woc : unit -> out_channel
+(** Names of the files used in windows implementation to communicate
+ http requests and html answers. Default "wserver.sin" and
+ "wserver.sou". Can have relative or absolute paths. *)
val sock_in : string ref
val sock_out : string ref
- (* Names of the files used in windows implementation to communicate
- http requests and html answers. Default "wserver.sin" and
- "wserver.sou". Can have relative or absolute paths. *)
+
+(** Name of the file whose presence tells the server to stop (at least
+ one request is necessary to unfreeze the server to make it check
+ that this file exits. Default "STOP_SERVER". Can have relative
+ or absolute path. *)
val stop_server : string ref
- (* Name of the file whose presence tells the server to stop (at least
- one request is necessary to unfreeze the server to make it check
- that this file exits. Default "STOP_SERVER". Can have relative
- or absolute path. *)
+
+(** CGI (Common Gateway Interface) mode (default false). *)
val cgi : bool ref
(* Example:
diff --git a/doc/html/.buildinfo b/doc/html/.buildinfo
new file mode 100644
index 0000000000..2b98416203
--- /dev/null
+++ b/doc/html/.buildinfo
@@ -0,0 +1,4 @@
+# Sphinx build info version 1
+# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
+config: 42e050ea871d04cb1c2c51413627c51c
+tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/doc/html/_images/diagram.png b/doc/html/_images/diagram.png
new file mode 100644
index 0000000000..4f171ed4ce
Binary files /dev/null and b/doc/html/_images/diagram.png differ
diff --git a/doc/html/_sources/dev-doc/binaries/bench.md.txt b/doc/html/_sources/dev-doc/binaries/bench.md.txt
new file mode 100644
index 0000000000..a6654e8e36
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/bench.md.txt
@@ -0,0 +1,5 @@
+# bench
+
+Executes multiple tests on the library and output CPU usage
+information.
+Can be executed with `make bench` or `_build/default/bench/bench.exe`.
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/cgl.md.txt b/doc/html/_sources/dev-doc/binaries/cgl.md.txt
new file mode 100644
index 0000000000..a8e0697ab8
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/cgl.md.txt
@@ -0,0 +1,4 @@
+# cgl
+
+Removes the links of an HTML page and replaces them by `span`s.
+Registered as a service.
diff --git a/doc/html/_sources/dev-doc/binaries/connex.md.txt b/doc/html/_sources/dev-doc/binaries/connex.md.txt
new file mode 100644
index 0000000000..c31fa7353d
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/connex.md.txt
@@ -0,0 +1,23 @@
+# connex
+
+## Documentation
+
+Calculates the connex components of a base and returns it in HTML.
+
+```
+usage: geneweb.connex
+
+ -gwd_p : Specify the port number of gwd (default = 2317); > 1024 for normal users.
+ -server : Name of the server (default is 127.0.0.1).
+ -a : all connex components
+ -s : produce statistics
+ -d : detail for this length
+ -i : ignore this file
+ -bf : by origin files
+ -del : ask for deleting branches whose size <= that value
+ -cnt : delete cnt branches whose size <= -del value
+ -exact : delete only branches whose size strictly = -del value
+ -o : output to this file
+ -help Display this list of options
+ --help Display this list of options
+```
diff --git a/doc/html/_sources/dev-doc/binaries/consang.md.txt b/doc/html/_sources/dev-doc/binaries/consang.md.txt
new file mode 100644
index 0000000000..68eb0500cd
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/consang.md.txt
@@ -0,0 +1,19 @@
+# consang
+
+## Documentation
+
+Calculates the consanguinity level of individuals and synchronizes it with
+the base.
+
+```
+usage: geneweb.consang [options]
+ -q quiet mode
+ -qq very quiet mode
+ -fast faster, but use more memory
+ -scratch : from scratch
+ -mem : Save memory, but slower when rewritting database
+ -nolock : do not lock database.
+```
+
+[Online documentation](https://geneweb.tuxfamily.org/wiki/consang)
+
diff --git a/doc/html/_sources/dev-doc/binaries/export.md.txt b/doc/html/_sources/dev-doc/binaries/export.md.txt
new file mode 100644
index 0000000000..2f998a5b4c
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/export.md.txt
@@ -0,0 +1,4 @@
+# export
+
+Exports a database in GED or in GW depending on the option.
+Registers the "EXPORT" request as an extra request.
diff --git a/doc/html/_sources/dev-doc/binaries/fixbase_plugin.md.txt b/doc/html/_sources/dev-doc/binaries/fixbase_plugin.md.txt
new file mode 100644
index 0000000000..b189e78c7d
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/fixbase_plugin.md.txt
@@ -0,0 +1,4 @@
+# fixbase
+
+Applies the different fixbase utils (module `Fixbase`) from a dedicated UI.
+Registers two new requests: FIXBASE and FIXBASE_OK.
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/forum.md.txt b/doc/html/_sources/dev-doc/binaries/forum.md.txt
new file mode 100644
index 0000000000..463f5b27b1
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/forum.md.txt
@@ -0,0 +1,5 @@
+# forum
+
+Includes a forum platform in the web server.
+Registers the requests FORUM, FORUM_ADD, FORUM_ADD_OK, FORUM_DEL, FORUM_P_P,
+FORUM_SEARCH, FORUM_VAL, FORUM_VIEW.
diff --git a/doc/html/_sources/dev-doc/binaries/ged2gwb.md.txt b/doc/html/_sources/dev-doc/binaries/ged2gwb.md.txt
new file mode 100644
index 0000000000..112d7210a4
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/ged2gwb.md.txt
@@ -0,0 +1,48 @@
+# ged2gwb
+
+## Documentation
+
+Imports a GEDCOM 5.5.1 file to a Geneweb base. GEDCOM is a popular format
+among genealogy enthusiasts.
+[GEDCOM 5.5.1 documentation](http://www.ancestris.org/dl/ancestris/norme_gedcom/Gedcom_norm_551_2019_11_15.pdf)
+
+```
+Usage: geneweb.ged2gwb [] [options] where options are:
+ -charset [ANSEL|ASCII|MSDOS] Force given charset decoding, overriding the possible setting in GEDCOM
+ -dates_dm Interpret months-numbered dates as day/month/year
+ -dates_md Interpret months-numbered dates as month/day/year
+ -ds Set the source field for persons and families without source data
+ -efn When creating a person, if the GEDCOM first name part holds several names, the first of this names becomes the person "first name" and the complete GEDCOM first name part a "first name alias".
+ -epn When creating a person, if the GEDCOM first name part looks like a public name, i.e. holds:
+* a number or a roman number, supposed to be a number of a nobility title,
+* one of the words: "der", "den", "die", "el", "le", "la", "the", supposed to be the beginning of a qualifier, then the GEDCOM first name part becomes the person "public name" and its first word his "first name".
+ -f Remove database if already existing
+ -fne When creating a person, if the GEDCOM first name part holds a part between 'b' (any character) and 'e' (any character), it is considered to be the usual first name: e.g. -fne '""' or -fne "()".
+ -lf Convert first names to lowercase letters, with initials in uppercase.
+ -log Redirect log trace to this file.
+ -ls Convert surnames to lowercase letters, with initials in uppercase. Try to keep lowercase particles.
+ -nc No consistency check
+ -no_efn Cancels the previous option.
+ -no_epn Cancels the previous option.
+ -no_nd Don't interpret a year preceded by a minus sign as a negative year
+ -no_pit Do not consider persons having titles as public
+ -nopicture Don't extract individual picture.
+ -o Output database (default: "a").
+ -particles Use the given file as list of particles
+ -rs_no_mention Force relation status to NoMention (default is Married)
+ -tnd Set negative dates when inconsistency (e.g. birth after death)
+ -trackid Print gedcom id to gw id matches.
+ -udi x-y Set the interval for persons whose death part is undefined:
+ - if before x years, they are considered as alive
+ - if after y year, they are considered as death
+ - between x and y year, they are considered as "don't know"
+ Default x is 80 and y is 120
+ -uin Put untreated GEDCOM tags in notes
+ -us Convert surnames to uppercase letters.
+```
+
+Depends on the library `gwexport`.
+
+## Our recommendations
+
+{doc}`/audit/binaries/ged2gwb`
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/gwb2ged.md.txt b/doc/html/_sources/dev-doc/binaries/gwb2ged.md.txt
new file mode 100644
index 0000000000..0123f647b8
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/gwb2ged.md.txt
@@ -0,0 +1,29 @@
+# gwb2ged
+
+## Documentation
+
+Converts a Geneweb database to a GEDCOM 5.5.1 file. GEDCOM is a popular format
+among genealogy enthusiasts.
+[GEDCOM 5.5.1 documentation](http://www.ancestris.org/dl/ancestris/norme_gedcom/Gedcom_norm_551_2019_11_15.pdf)
+
+
+```
+Usage: geneweb.gwb2ged [OPT]
+ -indexes export indexes in gedcom
+ -a maximum generation of the root's ascendants
+ -ad maximum generation of the root's ascendants descendants
+ -key key reference of root person. Used for -a/-d options. Can be used multiple times. Key format is "First Name.occ SURNAME"
+ -c : when a person is born less than years ago, it is not exported unless it is Public. All the spouses and descendants are also censored.
+ -charset [ASCII|ANSEL|ANSI|UTF-8] set charset; default is UTF-8
+ -d maximum generation of the root's descendants.
+ -mem save memory space, but slower.
+ -nn no (database) notes.
+ -nnn no notes (implies -nn).
+ -nopicture don't extract individual picture.
+ -o output file name (default: stdout).
+ -parentship select individuals involved in parentship computation between pairs of keys. Pairs must be defined with -key option, descendant first: e.g. -key "Descendant.0 SURNAME" -key "Ancestor.0 SURNAME". If multiple pair are provided, union of persons are returned.
+ -picture-path extract pictures path.
+ -s select this surname (option usable several times, union of surnames will be used).
+ -source replace individuals and families sources. Also delete event sources.
+ -v verbose
+```
diff --git a/doc/html/_sources/dev-doc/binaries/gwc.md.txt b/doc/html/_sources/dev-doc/binaries/gwc.md.txt
new file mode 100644
index 0000000000..8ede564135
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/gwc.md.txt
@@ -0,0 +1,30 @@
+# gwc
+
+## Documentation
+
+Creates a new database.
+
+```
+Usage: geneweb.gwc [options] [files]
+where [files] are a list of files:
+ source files end with .gw
+ object files end with .gwo
+and [options] are:
+ -bnotes [drop|erase|first|merge] Behavior for base notes of the next file. [drop]: dropped. [erase]: erase the current content. [first]: dropped if current content is not empty. [merge]: concatenated to the current content. Default: merge
+ -c Only compiling
+ -cg Compute consanguinity
+ -ds Set the source field for persons and families without source data
+ -f Remove database if already existing
+ -mem Save memory, but slower
+ -nc No consistency check
+ -nofail No failure in case of error
+ -nolock Do not lock database
+ -nopicture Do not create associative pictures
+ -o Output database (default: a.gwb)
+ -particles Particles file (default = predefined particles)
+ -q Quiet
+ -sep Separate all persons in next file
+ -sh Shift all persons numbers in next files
+ -stats Print statistics
+ -v Verbose
+```
diff --git a/doc/html/_sources/dev-doc/binaries/gwd.md.txt b/doc/html/_sources/dev-doc/binaries/gwd.md.txt
new file mode 100644
index 0000000000..e7dbcc89a0
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/gwd.md.txt
@@ -0,0 +1,318 @@
+# gwd
+
+## Documentation
+
+The main web-server for geneweb. It depends on `wserver`, a library defined in `bin/`.
+
+```
+Usage: gwd [options] where options are:
+ -a Select a specific address (default = any address of this computer).
+ -add_lexicon Add file as lexicon.
+ -allowed_tags HTML tags which are allowed to be displayed. One tag per line in file.
+ -auth Authorization file to restrict access. The file must hold lines of the form "user:password".
+ -bd Directory where the databases are installed.
+ -blang Select the user browser language if any.
+ -cache_langs Lexicon languages to be cached.
+ -cgi Force CGI mode.
+ -conn_tmout Connection timeout (default 120s; 0 means no limit).
+ -daemon Unix daemon mode.
+ -debug Enable debug mode
+ -digest Use Digest authorization scheme (more secure on passwords)
+ -friend Set a friend password.
+ -hd Directory where the directory lang is installed.
+ -images_dir Same than previous but directory name relative to current.
+ -images_url URL for GeneWeb images (default: gwd send them).
+ -lang Set a default language (default: fr).
+ -log Log trace to this file. Use "-" or "" to redirect output to stdout or "" to output log to stderr.
+ -log_level Send messages with severity <= to syslog (default: 7).
+ -login_tmout Login timeout for entries with passwords in CGI mode (default 1800s).
+ -max_clients Max number of clients treated at the same time (default: no limit) (not cgi).
+ -min_disp_req Minimum number of requests in robot trace (default: 6).
+ -no_host_address Force no reverse host by address.
+ -nolock Do not lock files before writing.
+ -only Only inet address accepted.
+ -p Select a port number (default = 2317).
+ -plugin .cmxs load a safe plugin. Combine with -force to enable for every base. Combine with -unsafe to allow unverified plugins. e.g. "-plugin -unsafe -force".
+ -plugins load all plugins in . Combine with -force to enable for every base. Combine with -unsafe to allow unverified plugins. e.g. "-plugins -unsafe -force".
+ -redirect Send a message to say that this service has been redirected to .
+ -robot_xcl , Exclude connections when more than requests in seconds.
+ -setup_link Display a link to local gwsetup in bottom of pages.
+ -trace_failed_passwd Print the failed passwords in log (except if option -digest is set).
+ -wd Directory for socket communication (Windows) and access count.
+ -wizard Set a wizard password.
+ -wjf Wizard just friend (permanently).
+```
+
+It is build from eight files: `gwdLog.ml`, `gwdPlugin.ml`, `request.ml` (these
+three files define `geneweb.gwd_lib`),, `gwdPluginDep.ml`,
+`gwdPluginMD5.ml`, `gwdPluginMETA.ml`, `robot.ml` and `gwd.ml`.
+
+### GwdLog module
+Util functions for logging errors & infos.
+
+### GwdPlugin module
+Module used for registering plug-ins to Gwd.
+
+### Request module
+The main interface between the Geneweb core and the rest of the world.
+
+### GedPluginDep module
+Calculates the load order of the different plugins.
+
+### GwdPluginMD5 module
+Checks the plugins .cmxs files and assets did not change since compilation.
+This module is written by mk_gwdPluginMD5.ml. After the plug-ins have been compiled,
+mk_gwdPluginMD5.ml lists them in a pattern matching with their MD5 hash.
+
+### GwdPluginMETA module
+Handles the metadata of a plug-in: its version, its mainteners' list and
+its dependencies.
+Metadata is optional, and may contain the dependencies of the plugin (separated
+by a comma), its maintainers (separated by a comma) and its version.
+
+### Robot module
+Handles the connection of robots to the server.
+
+### Gwd module
+The executable
+
+## Requests
+Gwd displays different web pages and perform different actions given a mode.
+This mode is given as a GET argument with the key `m`.
+
+Here is a quick documentation of each mode.
+
+* No mode: displays the default page depending on other arguments. It can be
+the index page (no base selected), a welcome page (only a base has been
+selected) or a person page (a base and a person has been selected).
+
+* Mode "A": displays the ascendants of the selected person (selection key is `i`).
+
+* Mode "ADD_FAM": displays the form for adding families.
+
+* Mode "ADD_FAM_OK": requests to add a new family.
+
+* Mode "ADD_IND": displays a form for adding a new person
+
+* Mode "ADD_IND_OK": requests to add a new person.
+
+* Mode "ADD_PAR": associate parents to a person (person identifier must be set
+with key `ip`)
+
+* Mode "ADD_PAR_OK" : requests to add new parents.
+
+* Mode "ANM" : displays the menu of anniversaries modification
+
+* Mode "AN" : displays anniversaries ; with the `v` key for months, displays
+the anniversaries of thay month (v=1 -> January, ...)
+
+* Mode "AD": displays death anniversaries ; with the `v` key for months, displays
+the anniversaries of thay month (v=1 -> January, ...)
+
+* Mode "AM": displays marriage anniversaries ; with the `v` key for months, displays
+the anniversaries of thay month (v=1 -> January, ...)
+
+* Mode "AS_OK": displays the results of an advanced search
+
+* Mode "C": displays the cousins menu
+
+* Mode "CAL": displays the calendars; if no key set, it will use today's date.
+
+* Mode "CHG_CHN": displays the form for changing children names of a person
+
+* Mode "CHG_CHN_OK": requests to change the children names a new person.
+
+* Mode "CHG_EVT_IND_ORD": displays the form for changing the order of events
+for a person
+
+* Mode "CHG_EVT_IND_ORD_OK": requests to change the evenement order of a
+person.
+
+* Mode "CHG_EVT_FAM_ORD": displays the form for changing the order of events
+for a family
+
+* Mode "CHG_EVT_FAM_ORD_OK": requests to change the evenement order of a
+family.
+
+* Mode "CHG_FAM_ORD": displays a menu to change the family order
+
+* Mode "CHG_FAM_ORD_OK": requests the family order change
+
+* Mode "CONN_WIZ": displays the connected wizards (the base admins)
+
+* Mode "D": displays the descendants of the selected person (selection key is `i`).
+
+* Mode "DAG": displays a relationship graph.
+
+* Mode "DEL_FAM": displays a page for validating the deletion of the family in
+argument (key is `i`).
+
+* Mode "DEL_FAM_OK": requests to remove a family.
+
+* Mode "DEL_IND": displays a page for validating the deletion of the person in
+argument (key is `i`).
+
+* Mode "DEL_IND_OK": requests to remove a person.
+
+* Mode "F": displays the family tree.
+
+* Mode "H": displays the file `fname.txt` where `fname` is register with the key
+`v` and the file being in `hd/etc` (`hd` is the dir specified by option `-hd`).
+
+* Mode "HIST": displays an history of updates.
+
+* Mode "HIST_CLEAN": displays the history list associated to the history file
+in argument (key is `f`).
+
+* Mode "HIST_CLEAN_OK": requests to clean the history associated to the history
+file in argument
+
+* Mode "HIST_DIFF": displays the page that allows to select (with variable
+`t = "SUM"`) and to view (with variable `t = "DIFF"`) the difference
+between all revisions of history file of concerned person in variable `f`.
+Intepretate the template file `updhist_diff.txt`
+
+* Mode "HIST_SEARCH": same as "HIST", but with a default search
+
+* Mode "IM": displays the image whose name is in argument (key is `s`)
+
+* Mode "IMH": same than "IM", but returns HTML
+
+* Mode "INV_FAM": displays a menu for inverting the order of two families (where
+a family is given by the `f` key and the individual is given by the `i` key).
+
+* Mode "INV_FAM_OK": requests to reverse the families.
+
+* Mode "KILL_ANC": Undocumented feature; kill someone's ancestors.
+
+* Mode "LB": lists the last births.
+
+* Mode "LD": lists the last deaths.
+
+* Mode "LINKED": displays links to pages assocaited to an individual.
+
+* Mode "LL": lists the persons who lived the longest.
+
+* Mode "LM": lists the last marriages.
+
+* Mode "MISC_NOTES": displays a menu to search in notes.
+
+* Mode "MISC_NOTES_SEARCH": same as "MISC_NOTES", but with a search argument
+ (key is `s`)
+
+* Mode "MOD_DATA": displays a menu for updating Geneweb's dictionary of names,
+last names, locations, sources and professions.
+
+* Mode "MOD_DATA_OK": requests a data modification.
+
+* Mode "MOD_FAM": displays a form for updating a family (key is `i`).
+
+* Mode "MOD_FAM_OK": requests a family modification.
+
+* Mode "MOD_IND": displays a form for updating a person (key is `i`).
+
+* Mode "MOD_IND_OK": requests a person modification.
+
+* Mode "MOD_NOTES": displays a text form for writing notes.
+
+* Mode "MOD_NOTES_OK": requests a note update.
+
+* Mode "MOD_WIZNOTES": displays the HTML page for editing wizard notes.
+Fails if wizard authentification is incorrect or if current user cannot
+edit.
+
+* Mode "MOD_WIZNOTES_OK": requests the wizard note modification.
+
+* Mode "MRG": displays a menu for merging two persons
+(key for persons is `i`).
+
+* Mode "MRG_DUP": displays a menu for merging possible duplications of persons
+(key for persons is `ip`).
+
+* Mode "MRG_DUP_IND_Y_N": either displays the merge dupliate menu if
+`answer_y` is not a key of the request, or a form for merging two
+person (whose keys are `i` and `i2`).
+
+* Mod "MRG_DUP_FAM_Y_N": same than "MRG_DUP_IND_Y_N", but for families.
+
+* Mode "MRG_FAM": displays a menu for merging families (family keys are `i`
+and `i2`). Couples must be identical (modulo reversion).
+
+* Mode "MRG_FAM_OK": requests the family merge
+
+* Mode "MRG_MOD_FAM_OK": requests the family modification and merge
+
+* Mode "MRG_IND": displays a form for merging two persons
+
+* Mode "MRG_IND_OK": requests a merge of two persons
+
+* Mode "MRG_MOD_IND_OK": requests a merge & modification of two persons
+
+* Mode "N": lists all the surnames; if a surname is selected (key is `v`),
+displays the list of pages where this surname is used.
+
+* Mode "NG": same than `N`, but expects a name with the key `n`
+
+* Mode "NOTES": displays the notes
+
+* Mode "OA": displays the list of the oldest persons that are still alive or,
+if unknown, whose death are not probable
+
+* Mode "OE": same as "OA", but for engaged couples
+
+* Mode "P": lists all the first names; if a surname is selected (key is `v`),
+displays the list of pages where this name is used.
+
+* Mode "PQP_PYR": displays a population pyramid (reachable from "STAT")
+
+* Mode "PS": displays all the persons associated to a given place (`ma` key for
+marriage, `bi` key for birth, `bp` key for baptism, `de` key for death and `bu`
+key for burial).
+
+* Mode "R": displays the relationship details between two persons
+
+* Mode "REQUEST": displays the current request
+
+* Mode "RL": displays the relationship link between two persons
+
+* Mode "RLM": displays relation ship details between multiple persons
+
+* Mode "S": displays the results of a search
+(from, for example, the main page).
+
+* Mode "SRC": displays the file in argument (key is `v`)
+
+* Mode "STAT": displays several links for statistics: latest births, death,
+marriages, the oldest couples, persons that are alive and who lives the longest.
+There also is a population pyramid.
+
+* Mode "CHANGE_WIZ_VIS": displays the connected wizards
+
+* Mode "TT": displays the titles associated to persons
+
+* Mode "U": displays the page associated to the template `updmenu.txt`
+
+* Mode "VIEW_WIZNOTES": Prints the HTML page displaying wizard notes
+
+* Mode "WIZNOTES": Same as VIEW_WIZNOTES, but fails if not authentificated
+
+* Mode "WIZNOTES_SEARCH": Same as VIEW_WIZNOTES, but highlights HTML with
+the specified string searched (key is `s`).
+
+Any other mode leads to an incorrect request page (Error 400).
+
+## Plug-ins
+Plus-ins are additional features that can be added to `gwd`. They can register
+two kind of services:
+
+* additional modes: with `GwdPlugin.register`, a plug-in can register new
+requests
+
+* additional computations: with `GwdPlugin.register_se`, a plug-in can register
+pre/post processors
+
+## Customization
+Each base can be customized with a `.gwf` file. A documented example is
+available on the `etc` directory at the root of the project.
+Plug-ins are activated for a base with the `plugin=...` directive in the
+configuration file.
diff --git a/doc/html/_sources/dev-doc/binaries/gwdiff.md.txt b/doc/html/_sources/dev-doc/binaries/gwdiff.md.txt
new file mode 100644
index 0000000000..869f1c5a81
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/gwdiff.md.txt
@@ -0,0 +1,17 @@
+# gwdiff
+
+## Documentation
+
+Target differences between two GeneWeb databases (see README for more details)
+
+```
+Usage: geneweb.gwdiff [options] base1 base2
+Options are:
+ -1 : (mandatory) defines starting person in base1
+ -2 : (mandatory) defines starting person in base2
+ -ad : checks descendants of all ascendants
+ -d : checks descendants (default)
+ -html : HTML format used for report
+ -mem : save memory space, but slower
+```
+
diff --git a/doc/html/_sources/dev-doc/binaries/gwfixbase.md.txt b/doc/html/_sources/dev-doc/binaries/gwfixbase.md.txt
new file mode 100644
index 0000000000..3b12587d66
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/gwfixbase.md.txt
@@ -0,0 +1,29 @@
+# fixbase
+
+## Documentation
+
+Checks the consistency of the base, fixes it and applies
+the latest patch if there is one.
+
+```
+Usage: geneweb.gwfixbase [OPTION] base
+ -dry-run do not commit changes (only print)
+ -q quiet mode
+ -qq very quiet mode
+ -fast fast mode. Needs more memory.
+ -families-parents missing doc
+ -families-children missing doc
+ -persons-NBDS missing doc
+ -persons-parents missing doc
+ -persons-families missing doc
+ -pevents-witnesses missing doc
+ -fevents-witnesses missing doc
+ -marriage-divorce missing doc
+ -person-key missing doc
+ -index rebuild index. It is automatically enable by any other option.
+ -invalid-utf8 missing doc
+```
+
+## Our recommendations
+
+- {doc}`/audit/binaries/gwfixbase`
diff --git a/doc/html/_sources/dev-doc/binaries/gwgc.md.txt b/doc/html/_sources/dev-doc/binaries/gwgc.md.txt
new file mode 100644
index 0000000000..867e55c4d4
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/gwgc.md.txt
@@ -0,0 +1,11 @@
+# gwgc
+
+## Documentation
+
+Simplifies a database by removing unused entries of its dictionary.
+
+```
+Usage: geneweb.gwgc [OPTION] base
+ --dry-run do not commit changes (only print)
+
+```
diff --git a/doc/html/_sources/dev-doc/binaries/gwrepl.md.txt b/doc/html/_sources/dev-doc/binaries/gwrepl.md.txt
new file mode 100644
index 0000000000..f78793826a
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/gwrepl.md.txt
@@ -0,0 +1,12 @@
+# gwrepl
+
+## Documentation
+
+Starts an OCaml interactive top-level for writing scripts manipulating the
+database. It loads all the necessary libraries to use the geneweb
+libraries.
+For script execution, run:
+ cat | [ GWREPL_PPF=/dev/null ] [ GWREPL_VERBOSE=1 ] [ GWREPL_FORCE_UNPACK=1 ] [ GWREPL_NOPROMPT=1 ] gwrepl.exe [scrip_arg1] ...
+
+For interactive top-level, run `gwdrepl.exe`.
+
diff --git a/doc/html/_sources/dev-doc/binaries/gwu.md.txt b/doc/html/_sources/dev-doc/binaries/gwu.md.txt
new file mode 100644
index 0000000000..1cf2f2e3c7
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/gwu.md.txt
@@ -0,0 +1,41 @@
+# gwu
+
+## Documentation
+
+Exports the content of a Geneweb base to a .gw file.
+
+```
+Usage: geneweb.gwu [OPT]
+ -odir create files from original name in directory (else on -o file)
+ -isolated export isolated persons (work only if export all database).
+ -old_gw do not export additional fields (for backward compatibility: < 7.00)
+ -raw raw output (without possible utf-8 conversion)
+ -sep <1st_name.num surname> To use together with the option "-odir": separate this person and all his ancestors and descendants sharing the same surname. All the concerned families are displayed on standard output instead of their associated files. This option can be used several times.
+ -sep_only_file with option "-sep", tells to separate only groups of that file.
+ -sep_limit When using the option "-sep", groups of families can become isolated in the files. Gwu reconnects them to the separated families (i.e. displays them to standard output) if the size of these groups is less than 21. The present option changes this limit.
+ -a maximum generation of the root's ascendants
+ -ad maximum generation of the root's ascendants descendants
+ -key key reference of root person. Used for -a/-d options. Can be used multiple times. Key format is "First Name.occ SURNAME"
+ -c : when a person is born less than years ago, it is not exported unless it is Public. All the spouses and descendants are also censored.
+ -charset [ASCII|ANSEL|ANSI|UTF-8] set charset; default is UTF-8
+ -d maximum generation of the root's descendants.
+ -mem save memory space, but slower.
+ -nn no (database) notes.
+ -nnn no notes (implies -nn).
+ -nopicture don't extract individual picture.
+ -o output file name (default: stdout).
+ -parentship select individuals involved in parentship computation between pairs of keys. Pairs must be defined with -key option, descendant first: e.g. -key "Descendant.0 SURNAME" -key "Ancestor.0 SURNAME". If multiple pair are provided, union of persons are returned.
+ -picture-path extract pictures path.
+ -s select this surname (option usable several times, union of surnames will be used).
+ -source replace individuals and families sources. Also delete event sources.
+ -v verbose
+
+```
+
+See also:
+https://geneweb.tuxfamily.org/wiki/gwu
+https://geneweb.tuxfamily.org/wiki/save
+
+## Our recommendations
+
+{doc}`/audit/binaries/gwu`
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/gwxjg.md.txt b/doc/html/_sources/dev-doc/binaries/gwxjg.md.txt
new file mode 100644
index 0000000000..59baf34347
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/gwxjg.md.txt
@@ -0,0 +1,5 @@
+# gwxjg
+
+WARNING: This is not a plugin, it only provides tools for other plugins to use.
+This package provides translation tools from Geneweb structure to Jingoo's type system.
+
diff --git a/doc/html/_sources/dev-doc/binaries/index.rst.txt b/doc/html/_sources/dev-doc/binaries/index.rst.txt
new file mode 100644
index 0000000000..426c0fc991
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/index.rst.txt
@@ -0,0 +1,9 @@
+Project binaries
+================
+
+.. toctree::
+ :maxdepth: 2
+
+ official_binaries
+ plugins
+ tests
diff --git a/doc/html/_sources/dev-doc/binaries/jingoo.md.txt b/doc/html/_sources/dev-doc/binaries/jingoo.md.txt
new file mode 100644
index 0000000000..dd72219f28
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/jingoo.md.txt
@@ -0,0 +1,3 @@
+# jingoo
+
+WARNING: This is not a plugin. This only loads `geneweb.gwd_lib`.
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/lib_show.md.txt b/doc/html/_sources/dev-doc/binaries/lib_show.md.txt
new file mode 100644
index 0000000000..6b0dc8d6f3
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/lib_show.md.txt
@@ -0,0 +1,3 @@
+# lib_show
+
+WARNING: This is not a plugin. This only loads `geneweb.def_show`.
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/no_index.md.txt b/doc/html/_sources/dev-doc/binaries/no_index.md.txt
new file mode 100644
index 0000000000..41ea17b895
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/no_index.md.txt
@@ -0,0 +1 @@
+# no_index
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/official_binaries.rst.txt b/doc/html/_sources/dev-doc/binaries/official_binaries.rst.txt
new file mode 100644
index 0000000000..4f8960bb36
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/official_binaries.rst.txt
@@ -0,0 +1,24 @@
+Official binaries
+=================
+
+These binaries are from the official geneweb package. They provide
+the core utilities for manipulating databases and a web server.
+
+.. toctree::
+ :maxdepth: 2
+ :caption: List of binaries:
+
+ connex
+ consang
+ ged2gwb
+ gwb2ged
+ gwb2ged
+ gwc
+ gwd
+ gwdiff
+ gwfixbase
+ gwgc
+ gwrepl
+ gwu
+ setup
+ update_nldb
diff --git a/doc/html/_sources/dev-doc/binaries/plugins.rst.txt b/doc/html/_sources/dev-doc/binaries/plugins.rst.txt
new file mode 100644
index 0000000000..ae346c37cd
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/plugins.rst.txt
@@ -0,0 +1,22 @@
+Plug-ins
+========
+
+Plug-ins are user-defined scripts that were merged into the main repository.
+While they are part of the main branch, their purpose it to let user develop
+their own OCaml scripts for updating the behaviour or the web werver `gwd`.
+
+.. toctree::
+ :caption: List of plug-ins:
+
+ cgl
+ export
+ fixbase_plugin
+ forum
+ gwxjg
+ jingoo
+ lib_show
+ no_index
+ v7
+ v7_im
+ welcome
+ xhtml
diff --git a/doc/html/_sources/dev-doc/binaries/setup.md.txt b/doc/html/_sources/dev-doc/binaries/setup.md.txt
new file mode 100644
index 0000000000..09fe5965e7
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/setup.md.txt
@@ -0,0 +1,15 @@
+# setup
+
+The database setup portal. It depends on `wserver`, a library defined in `bin/`.
+
+```
+Usage: setup.exe [options] where options are:
+ -bd : Directory where the databases are installed.
+ -gwd_p : Specify the port number of gwd (default = 2317); > 1024 for normal users.
+ -lang : default lang
+ -daemon : Unix daemon mode.
+ -p : Select a port number (default = 2316); > 1024 for normal users.
+ -only : File containing the only authorized address
+ -gd : gwsetup directory
+ -bindir : binary directory (default = value of option -gd)
+```
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/test.md.txt b/doc/html/_sources/dev-doc/binaries/test.md.txt
new file mode 100644
index 0000000000..fa696cd990
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/test.md.txt
@@ -0,0 +1,38 @@
+# test
+
+Starts multiple non-regression tests. Tested modules are `MUtil`, `Util`
+(`Test_utils`), the choosen `Sosa` implementation (`Test_sosa`), `Place`
+(`Test_place`) and `NotesLink` (`Test_wiki`).
+Can be executed with `make test` or `_build/default/test/test.exe`.
+
+```
+usage: default/test/test.exe options*
+ -conf fn Read configuration file.
+ -cache-filename str Cache file to store previous results. (default: /home/ovenstent/gits/geneweb-project/geneweb/_build/oUnit-$(suite_name).cache)
+ -chooser {failfirst|simple}
+ Select the method to choose tests to run. (default: simple)
+ -ci {true|false} Display logs for CI, like Travis and AppVeyor, in the console with colors. (default: false)
+ -display {true|false} Output logs on screen. (default: true)
+ -health-check-interval f Seconds between checking health of workers. (default: 1.)
+ -log-encoding str Encoding of the log. (default: utf-8)
+ -no-cache-filename Reset value of cache_filename.
+ -no-output-file Reset value of output_file.
+ -no-output-html-dir Reset value of output_html_dir.
+ -no-output-junit-file Reset value of output_junit_file.
+ -no-testdata-dir Reset value of testdata_dir.
+ -output-file str Output verbose log in the given file. (default: /home/ovenstent/gits/geneweb-project/geneweb/_build/oUnit-$(suite_name)-$(shard_id).log)
+ -output-html-dir str Output directory of the HTML files. (default: none)
+ -output-junit-file str Output file for JUnit. (default: none)
+ -processes-grace-period f Delay to wait for a process to stop. (default: 5.)
+ -processes-kill-period f Delay to wait for a process to stop after killing it. (default: 5.)
+ -results-style-1-X {true|false} Use OUnit 1.X results printer (will be deprecated in 2.1.0+). (default: false)
+ -run-gc-full-major {true|false} Run a Gc.full_major in between tests. (default: true)
+ -runner {processes|sequential}
+ Select a the method to run tests. (default: processes)
+ -shards i Number of shards to use as worker (threads or processes). (default: 4)
+ -suite-name str The name of the test suite running. (default: Geneweb)
+ -testdata-dir str Location of the test data directory (absolute path). (default: none)
+ -verbose {true|false} Run test in verbose mode. (default: false)
+ -only-test path Run only the selected tests.
+ -list-test List tests
+```
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/tests.rst.txt b/doc/html/_sources/dev-doc/binaries/tests.rst.txt
new file mode 100644
index 0000000000..18a1504898
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/tests.rst.txt
@@ -0,0 +1,9 @@
+Tests
+=====
+
+.. toctree::
+ :maxdepth: 2
+ :caption: List of binaries:
+
+ test
+ bench
diff --git a/doc/html/_sources/dev-doc/binaries/update_nldb.md.txt b/doc/html/_sources/dev-doc/binaries/update_nldb.md.txt
new file mode 100644
index 0000000000..81e1d8837a
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/update_nldb.md.txt
@@ -0,0 +1 @@
+# update_nldb
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/v7.md.txt b/doc/html/_sources/dev-doc/binaries/v7.md.txt
new file mode 100644
index 0000000000..4da3171a7a
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/v7.md.txt
@@ -0,0 +1,4 @@
+# v7
+
+Defines new reqursts and redefines some old ones with new changes for the V7.
+Registers the requests to the index (""), A, C, D, DOC, L, P, PS and TP.
diff --git a/doc/html/_sources/dev-doc/binaries/v7_im.md.txt b/doc/html/_sources/dev-doc/binaries/v7_im.md.txt
new file mode 100644
index 0000000000..245c2acb40
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/v7_im.md.txt
@@ -0,0 +1,4 @@
+# v7_im
+
+Complements plugin v7 with image handling.
+Registers requests DEL_IMAGE, DEL_IMAGE_OK, SND_IMAGE and SND_IMAGE_OK.
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/welcome.md.txt b/doc/html/_sources/dev-doc/binaries/welcome.md.txt
new file mode 100644
index 0000000000..3ee9ebe53d
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/welcome.md.txt
@@ -0,0 +1,4 @@
+# welcome
+
+This plugin facilitates the main page and search page use.
+Registers the requests to the index page and S.
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/binaries/xhtml.md.txt b/doc/html/_sources/dev-doc/binaries/xhtml.md.txt
new file mode 100644
index 0000000000..3486301b7a
--- /dev/null
+++ b/doc/html/_sources/dev-doc/binaries/xhtml.md.txt
@@ -0,0 +1,3 @@
+# xhtml
+
+Replaces the `Content-type` field of requests by `xhtml+xml`.
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/geneweb-lib/index.md.txt b/doc/html/_sources/dev-doc/geneweb-lib/index.md.txt
new file mode 100644
index 0000000000..30940f2d67
--- /dev/null
+++ b/doc/html/_sources/dev-doc/geneweb-lib/index.md.txt
@@ -0,0 +1,112 @@
+# Geneweb library overview
+
+## Def lib
+
+### Adef
+
+This module exposes base types and functions used by many other geneweb's libraries. It defines the types and manipulation functions for:
+- Consanguinity degrees
+- Dates
+- Compressed dates
+- Polymorphic couple that regroups parents (father and mother).
+
+### Def
+## Util lib
+
+### Buff
+
+Module that defines extenisable buffer and auxilarry functions to store charcacters and strings and get buffer's content. The reason to define this module instead of using standard [Buffer](https://docs.ocaml.pro/docs/LIBRARY.stdlib@ocaml-base-compiler.4.10.0/Stdlib/Buffer/index.html) module is that its
+storing functions are more simple to use as an argument to the recursive functions over strings that are very common throughout Geneweb library.
+
+### Name
+
+Module that defines some useful functions to construct, format, compress and adopt the strings to represent them as being person surnames/first names.
+
+### Utf8
+
+Utf8 is a module that regroups some standard and useful functions dealing with UTF-8 encoded strings, which differs to standard OCaml module [Stdlib.String](https://docs.ocaml.pro/docs/LIBRARY.stdlib@ocaml-base-compiler.4.10.0/Stdlib/String/index.html), that internally considers the string as a table of bytes (Utf8 consider it as a table of UTF-8 encoded character). The major difference of this module comparing to the similar (like [Camomile](http://camomile.sourceforge.net/)) is that sometimes it makes use of [OCaml implementation](https://github.com/geneweb/unidecode) for [Unidecode](https://pypi.org/project/Unidecode/) library, that is used to convert encoded UTF-8 character to its ASCII representation.
+
+### Secure
+
+This module grants additional security layer and controls which directories can be read by the webserver, to prevent malicious users from reading/writing on the server disk. Allowed directories are composed of list of **assets** and the **base directory**.
+
+### Opt
+
+Module that defines/redefines some useful functions over `option` type.
+
+### Lock
+
+Module that define the flag and the functions that allow
+to manage simultaneous accesses to the files by puting a lock on them with `Unix.lockf`.
+
+### ProgrBar
+
+Module that provides printing functions that helps to iterate over collections with progressive bar that shows current iteration stage (proportion of current index to total number). Here is an exemple how does it used for iteration over collection with 240 elements :
+
+```bash
+
+# start ()
+............................................................
+
+# run 0 240
+|...........................................................
+
+# run 1 240
+/...........................................................
+
+# run 2 240
+-...........................................................
+
+# run 3 240
+\...........................................................
+
+# run 4 240
+o|..........................................................
+
+# sucessive runs
+
+# run 120 240
+
+oooooooooooooooooooooooooooooo|.............................
+
+```
+
+### Mutil
+
+The main module inside *Util* library that regroups many type of useful functions dealing with :
+- File system
+- Lists
+- Arrays
+- Names
+- Letters
+- IO
+- System calls
+- Parsing different formats (like lexicon.txt or HTTP header)
+- Encoding/decoding
+
+
+### Calendar
+
+This module defines bindings to [calendars](https://github.com/geneweb/calendars) package that allows to manage different calendar's date. Module exposes functions that:
+- Convert SDN (serial day number) to and from one of the date for supported calendars (gregorian, julian, french andhebrew).
+- Convert any supported calendar's date to and from gregorian's date.
+- Give information about moon's phase and about lunar calendar for the given day (SDN).
+
+### Date
+
+*Date* provides usefull functions that helps to compare and to count time elapes between two dates.
+
+### Pqueue
+
+This module implements priority queues. All operations are purely applicative (no side effects).
+The implementation uses [binomial queues from Chris Okasaki](https://lara.epfl.ch/w/_media/fv20/optimalpurelyfunctionalqueues.pdf). Functions `add`, `take` and `union` are in O(log n) in the worst case.
+
+### Futil
+
+This module regroups some usefull functions dealing with polymorphic data types (starts with `gen_`). Particulary it exposes functions that map values inside `gen_` data type. Aditionally it provide `gen_person_misc_names` that computes list of various mix between all kind of name of the person.
+
+## Gwdb-legacy lib
+
+### Btree
+
+Module that is equivalent to [Stdlib.Map](https://ocaml.org/api/Map.html) except some added in output signature functions dealing with keys.
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/index.rst.txt b/doc/html/_sources/dev-doc/index.rst.txt
new file mode 100644
index 0000000000..b4000c0ddb
--- /dev/null
+++ b/doc/html/_sources/dev-doc/index.rst.txt
@@ -0,0 +1,23 @@
+
+Developer documentation
+========================
+
+This documentation is an overview of the different components of Geneweb.
+It describes:
+
+- The functioning of the build system;
+
+- An overview of some libraries defined in the project;
+
+- A documentation of the main binaries;
+
+- A documentation for libraries generated with **odoc**.
+
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+ overview/index
+ installation/index
+ binaries/index
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/installation/build-system.md.txt b/doc/html/_sources/dev-doc/installation/build-system.md.txt
new file mode 100644
index 0000000000..3711914f8b
--- /dev/null
+++ b/doc/html/_sources/dev-doc/installation/build-system.md.txt
@@ -0,0 +1 @@
+dsd
\ No newline at end of file
diff --git a/doc/html/_sources/dev-doc/installation/index.md.txt b/doc/html/_sources/dev-doc/installation/index.md.txt
new file mode 100644
index 0000000000..4a604eadbe
--- /dev/null
+++ b/doc/html/_sources/dev-doc/installation/index.md.txt
@@ -0,0 +1,824 @@
+# Installation procedure
+
+## Build documentation
+1. Install the dependencies in an opam switch
+ ```
+ $ opam switch create geneweb 4.09.1
+ $ opam install . --deps-only
+ ```
+
+2. Run the configuration script
+ ```
+ $ ocaml ./configure.ml
+ ```
+
+3. Build the distibution
+ ```
+ $ make clean distrib
+ ```
+
+## Configure
+The `configure.ml` file is an ocaml script whose purpose is to
+update `Makefile.config` by setting the environment variables
+the `Makefile` needs.
+
+```
+usage: configure.ml [options]
+ --gwdb-legacy Use legacy backend
+ --release Use release profile: no debug informations (defaut: true)
+ --debug Use dev profile: no optimization, debug informations (default: false)
+ --sosa-legacy Use legacy Sosa module implementation
+ --sosa-num Use Sosa module implementation based on `num` library
+ --sosa-zarith Use Sosa module implementation based on `zarith` library
+ --syslog Log gwd errors using syslog
+ -help Display this list of options
+ --help Display this list of options
+```
+
+## Build system
+
+### Makefile
+
+The root of every action that could be performed on geneweb pass over its **Makefile**. In this section we will describe the variables, targets and its dependencies.
+
+#### Variables
+
+Makefile loads variables from **Makefile.config** generated by the configuration script for customised execution of some commands. Variables with suffix `_D` are passed to [cppo](https://github.com/ocaml-community/cppo) command that preprocess files with an equivalent to the C preprocessor. Here is a description of those variables :
+
+- `OS_TYPE` : kernel name (Linux, Win, Darwin, etc.)
+- `STRIP` : command that discards symbols from compiled object files. Depends on kernel (linux command is ```strip```).
+- `RM` : command that is used to remove files, directories, etc. Depends on kernel (linux command is ```/bin/rm -f```).
+- `EXT` : extension for geneweb executable files. Depends on kernel (on Windows it's *.exe*).
+- `GWDB_D` : argument to cppo command that defines a variable that tells which geneweb data base driver implementation will be used (example : *GENEWEB_GWDB_LEGACY*).
+- `OS_D` : argument to cppo command that defines a variable for current system (example : *UNIX*).
+- `SYSLOG_D` : argument to cppo command that could define a marker variable *SYSLOG*. When defined, activate logging by geneweb server.
+- `GWDB_PKG` : dune library that implements geneweb database driver (example : *geneweb.gwdb-legacy*).
+- `SOSA_PKG` : dune library that implements sosa (example : *geneweb_sosa_zarith*).
+- `SYSLOG_PKG` : if *SYSLOG* is defined, then indicate dune library used for logging functionalities.
+- `DUNE_DIRS_EXCLUDE` : specifies directories that will be excluded from *geneweb* library. Used to exclude not chosen implementations for sosa and for geneweb database driver.
+- `DUNE_PROFILE` : specifies dune profile (default : release).
+
+Makefile also defines additional non-configurable variables :
+
+- `PREFIX` :
+- `DISTRIB_DIR` : directory where a local geneweb distribution is stored.
+- `BUILD_DISTRIB_DIR` : directory where geneweb executable files for local distribution are stored.
+- `BUILD_DIR` : directory where the compiled artefacts are stored.
+- `CPPO_D` : regroups all arguments to cppo (`GWDB_D`, `OS_D`, `SYSLOG_D`). If dune's profile is *dev* then also contains the definition of *DEBUG* variable that enables emitting warnings on stderr by geneweb server.
+- `BENCH_FILE` : file where benchmark results are stored.
+
+Nowadays geneweb is entirely compiled by [dune](https://dune.readthedocs.io/en/stable/index.html), due to *dune* files. Non-preprocessed version of *dune* files are stored in *dune.in*. Makefile is responsible for the generation of corresponding *dune* from the *dune.in*. Every *dune* file that should be generated is stored in variable `GENERATED_FILES_DEP` in addition to other files generated by Makefile :
+
+```Makefile
+GENERATED_FILES_DEP = \
+ dune-workspace \
+ hd/etc/version.txt \
+ lib/dune \
+ lib/gwdb/dune \
+ lib/core/dune \
+ lib/gwlib.ml \
+ lib/util/dune \
+ benchmark/dune \
+ bin/connex/dune \
+ bin/consang/dune \
+ bin/fixbase/dune \
+ bin/ged2gwb/dune \
+ bin/gwb2ged/dune \
+ bin/gwc/dune \
+ bin/gwd/dune \
+ bin/gwdiff/dune \
+ bin/gwgc/dune \
+ bin/gwrepl/dune \
+ bin/gwrepl/.depend \
+ bin/gwu/dune \
+ bin/setup/dune \
+ bin/update_nldb/dune \
+ test/dune \
+```
+
+#### Targets
+
+##### Main targets
+
+- Makefile.config: configure.ml
+
+ Executed when *Makefile.config* isn't up-to-date with *configure.ml*. Prints corresponding error message and terminates execution. This target isn't executed when the goal is `ci` target.
+
+- lib/gwlib.ml:
+
+ Generates the content for *lib/gwlib.ml* that depends on `PREFIX`.
+
+- bin/gwrepl/.depend:
+
+ Generates the content for *bin/gwrepl/.depend*.
+
+- hd/etc/version.txt:
+
+ Generates the content for hd/etc/version.txt that indicates some information about geneweb version like date of compilation and commit used.
+
+- %/dune: %/dune.in Makefile.config
+
+ Generates [dune](https://dune.readthedocs.io/en/stable/dune-files.html#dune) file from corresponding *dune.in* as follows:
+ - Preprocess with ```cppo -n``` depending on `CPPO_D` argument.
+ - Remplaces with [sed](https://www.gnu.org/software/sed/manual/sed.html) occurrences of %%%CPPO_D%%%, %%%SOSA_PKG%%%, %%%GWDB_PKG%%%, %%%SYSLOG_PKG%%% and %%%DUNE_DIRS_EXCLUDE%%% by the values of corresponding variables.
+
+- dune-workspace: dune-workspace.in Makefile.config
+
+ Generates [dune-workspace](https://dune.readthedocs.io/en/stable/dune-files.html#dune-workspace) file that is used to define different build contexts and currently selected build profile. Generation is done by replacing the occurrence of %%%DUNE_PROFILE%%% inside *dune-workspace.in* by the value of corresponding variable.
+
+- build: $(GENERATED_FILES_DEP)
+
+ Default goal when ```make``` is called without targets. Executes the target for every file specified in `GENERATED_FILES_DEP` and then build all public artefacts in geneweb package.
+
+- distrib: build
+
+ Principal target. Build entire system and creates a local distibution of geneweb under `DISTRIB_DIR` directory. See for more information.
+
+- clean:
+
+ Removes all generated files that are specified in `GENERATED_FILES_DEP` and `DISTRIB_DIR`
+
+##### Additional targets
+
+- generated: $(GENERATED_FILES_DEP)
+
+ Executes the target for every file specified in `GENERATED_FILES_DEP`.
+
+- install: $(GENERATED_FILES_DEP)
+
+ Executes the target for every file specified in `GENERATED_FILES_DEP`, compilates all public artefacts and then install geneweb in opam.
+
+- uninstall: $(GENERATED_FILES_DEP)
+
+ Same as **install** but remote geneweb from opam.
+
+- doc: | $(GENERATED_FILES_DEP)
+
+ Generates documentation for all public artefacts in geneweb. Note that prerequisites on the right to `|` are [order-only](https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html).
+
+- test: | $(GENERATED_FILES_DEP)
+
+ Executes test suites defined in *test* directory.
+
+- bench: | $(GENERATED_FILES_DEP)
+
+ Executes the benchmark defined in *benchmark* directory.
+
+- bench-marshal: | $(GENERATED_FILES_DEP)
+
+ Executes the benchmark with options ```--marshal --name ${BENCH_NAME} ${BENCH_FILE}``` where `BENCH_NAME` is the name of bench results that will be stored in hash table and marshalled in `BENCH_FILE`. Prerequisite to **ci** target.
+
+- bench-tabulate: | $(GENERATED_FILES_DEP)
+
+ Executes the benchmark with option ```--tabulate ${BENCH_FILE}``` that prints on the screen tables of bench results stored in `BENCH_FILE`. Prerequisite to **ci** target.
+
+- ci:
+
+ - Configure geneweb for every existing implementation of sosa.
+ - Executes tests and benchmarks (name corresponds to sosa implementation) for each of them (recursive make call with goals **clean**, **test**, **bench-marshal** **clean**).
+ - Prints all benchmark results (recursive make call with goal **bench-tabulate**).
+
+### Dune configuration files
+
+#### dune-project
+
+This file is used to mark the root of projects as well as define project-wide parameters. In the case of geneweb this file specifies version of the dune configuration files with `lang` stanza and sets the name of project with `name` stanza:
+
+```lisp
+(lang dune 2.8)
+(name geneweb)
+```
+
+#### dune-workspace
+
+This file is used to define different build contexts and to select a build profile.
+Geneweb defines only one `default` context. Build profile (`release` or `dev`) is chosen by configuration script (```--release``` option to use release profile) with subsequent ```make```.
+
+#### dune
+
+Dune files used to describe libraries, executables, tests, and everything dune needs to know about. Some of dune files are preprocessed by Makefile in order to remplace all occurences of %%%VARIABLE%%% by their real value (see [Variables](#variables)) In this section we will describe each of those file inside geneweb project.
+
+```./dune```
+
+The root dune file that, in the geneweb case, modifies the environment used with `dev` build profile. Particularly, it passes additional [option](https://ocaml.org/manual/native.html#s:native-options) to the OCaml compiler :
+
+```bash
+-w +a-4-9-35-42-44-48
+```
+
+This option tells to compiler to enable all the warnings except :
+- Fragile pattern matching.
+- Missing fields in a record pattern.
+- Unused for-loop index.
+- Disambiguated constructor or label name.
+- Open statement that shadows an already defined identifier.
+- Implicit elimination of optional arguments.
+
+#### Geneweb library
+
+```lib/dune```
+
+Defines public **geneweb** library .
+
+- It includes modules under the `lib` directory and every of them is preprocessed with ```cppo```.
+- Module *TemplAst* is specified as a module without implementation (has only .mli).
+- Module *Templ_parser* is obtained from *templ_parser.mll* with stanza `ocamllex` that makes call for [ocamllex](https://ocaml.org/manual/lexyacc.html#s:ocamllex-overview) lexer.
+- Compilation of this library includes compilation of every subdirectory (with `dirs` stanza) except those mentioned in %%%DUNE_DIRS_EXCLUDE%%% variable (not used implementations).
+- Depends on geneweb libraries :
+ - geneweb_core
+ - geneweb_def
+ - geneweb_gwdb
+ - geneweb_sosa_mli
+ - geneweb_util
+- Depends on extrenal libraries :
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+ - [stdlib-shims](https://docs.ocaml.pro/docs/META.stdlib-shims@stdlib-shims.0.3.0/index.html)
+ - [camlp5](https://docs.ocaml.pro/docs/META.camlp5@camlp5.8.00.01/index.html)
+ - [markup](https://docs.ocaml.pro/docs/META.markup@markup.1.0.0-1/index.html)
+
+```lib/core/dune```
+
+Defines public **geneweb.core** library (locally - **geneweb_core**).
+
+- It includes modules under the `lib/core` directory and every of them is preprocessed with ```cppo```.
+- Depends on geneweb libraries :
+ - geneweb_def
+ - geneweb_gwdb
+ - geneweb_sosa_mli
+ - geneweb_util
+- Depends on extrenal libraries :
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```lib/def/dune```
+
+Defines public **geneweb.def** library (locally - **geneweb_def**). Includes only two modules : *Adef* and *Def*.
+
+```lib/gwdb/dune```
+
+Defines public **geneweb.gwdb** library (locally - **geneweb_gwdb**).
+
+- It includes modules under the `lib/gwdb` directory and every of them is preprocessed with ```cppo```.
+- Depends on geneweb libraries :
+ - geneweb_def
+ - gwdb_driver_mli
+ - geneweb_util
+- Depends on extrenal libraries :
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```lib/gwdb_driver.mli/dune```
+
+Defines public virtual (that requires an implementation) **geneweb.gwdb_driver** library (locally - **gwdb_driver_mli**).
+
+- Include only one virtual module *Gwdb_driver*.
+- Depends on geneweb libraries :
+ - geneweb_def
+- Depends on extrenal libraries :
+ - [re](https://docs.ocaml.pro/docs/META.re@re.1.9.0/index.html)
+
+```lib/gwdb-legacy/dune```
+
+Defines public **geneweb.gwdb-legacy** library (locally - **gwdb_legacy**). This library is an implementaion for virtual **gwdb_driver_mli** library.
+
+- It includes modules under the `lib/gwdb-legacy` directory.
+- Module *Dbdisk* is specified as a module without implementation.
+- Depends on geneweb libraries :
+ - geneweb_def
+ - geneweb_util
+- Depends on extrenal libraries :
+ - [re](https://docs.ocaml.pro/docs/META.re@re.1.9.0/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```lib/json_export/dune```
+
+Defines public **geneweb.export** library (locally - **geneweb_export**).
+
+- Include only one module *Json_converter*.
+- Depends on geneweb libraries :
+ - geneweb_def
+ - geneweb_def_show
+ - geneweb_gwdb
+- Depends on extrenal libraries :
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```lib/show/dune```
+
+Defines public **geneweb.def_show** library (locally - **geneweb_def_show**).
+
+- Include only one module *Def_show*. This module is preprocessed with [ppx_import](https://github.com/ocaml-ppx/ppx_import) and [ppx_deriving.show](https://github.com/ocaml-ppx/ppx_deriving#plugin-show) PPX rewriters that require feedback from the compilation phase (for that it is used inside `staged_pps` stanza rather than in ordinar `pps`).
+- Depends on geneweb libraries :
+ - geneweb_def
+ - geneweb_gwdb
+
+```lib/sosa.mli/dune```
+
+Defines public virtual **geneweb.sosa.mli** library (locally - **geneweb_sosa_mli**). Includes only one virtual module *Sosa*.
+
+```lib/sosa_array/dune```
+
+Defines public **geneweb.sosa_array** library (locally - **geneweb_sosa_array**). This library is an one of implementaions for virtual **geneweb_sosa_mli** library. It includes only one module *Sosa*.
+
+```lib/sosa_num/dune```
+
+Defines public **geneweb.sosa_num** library (locally - **geneweb_sosa_num**). This library is an one of implementaions for virtual **geneweb_sosa_mli** library.
+
+- It includes only one module *Sosa*.
+- Depends on extrenal libraries :
+ - [num](https://docs.ocaml.pro/docs/META.num@num.1.4/index.html)
+
+```lib/sosa_zarith/dune```
+
+Defines public **geneweb.sosa_zarith** library (locally - **geneweb_sosa_zarith**). This library is an one of implementaions for virtual **geneweb_sosa_mli** library.
+
+- It includes only one module *Sosa*.
+- Depends on extrenal libraries :
+ - [zarith](https://docs.ocaml.pro/docs/META.zarith@zarith.1.11/index.html)
+
+```lib/util/dune```
+
+Defines public **geneweb.util** library (locally - **geneweb_util**).
+
+- It includes modules under the `lib/util` directory and every of them is preprocessed with ```cppo```.
+- Depends on geneweb libraries :
+ - geneweb_def
+- Depends on extrenal libraries :
+ - [calendars](https://docs.ocaml.pro/docs/META.calendar@calendar.2.04/index.html)
+ - [stdlib-shims](https://docs.ocaml.pro/docs/META.stdlib-shims@stdlib-shims.0.3.0/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+ - [unidecode](https://github.com/geneweb/unidecode)
+ - [re](https://docs.ocaml.pro/docs/META.re@re.1.9.0/index.html)
+ - [uucp](https://docs.ocaml.pro/docs/META.uucp@uucp.13.0.0/index.html)
+ - [uunf](https://docs.ocaml.pro/docs/META.uunf@uunf.13.0.0/index.html)
+ - [uutf](https://docs.ocaml.pro/docs/META.uutf@uutf.1.0.2/index.html)
+
+##### Geneweb executables
+
+```bin/connex/dune```
+
+Defines public **geneweb.connex** executable (locally - **connex**).
+
+- It includes only one module *Connex*.
+- Depends on geneweb libraries :
+ - geneweb
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+- Depends on extrenal libraries :
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```bin/consang/dune```
+
+Defines public **geneweb.consang** executable (locally - **consang**).
+
+- It includes only one module *Consang* that is preprocessed with ```cppo```.
+- Depends on geneweb libraries :
+ - geneweb
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+- Depends on extrenal libraries :
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```bin/fixbase/dune```
+
+Defines public **geneweb.gwfixbase** executable (locally - **gwfixbase**).
+
+- It includes only one module *Gwfixbase*.
+- Depends on geneweb libraries :
+ - geneweb
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+- Depends on extrenal libraries :
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```bin/ged2gwb/dune```
+
+Defines public **geneweb.ged2gwb** executable (locally - **ged2gwb**).
+
+- It includes only one module *Ged2gwb* preprocessed by [camlp5o](https://camlp5.github.io/doc/html/) that provides parsing and pretty printing tools. Particulary, it uses following extensions:
+ - `pr_o.cmo`: pretty print in normal syntax
+ - `pa_extend.cmo`: syntax extension for grammars
+ - `q_MLast.cmo`: syntax tree nodes (in revised syntax)
+- Depends on geneweb libraries :
+ - geneweb
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+- Depends on extrenal libraries :
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```bin/gwb2ged/dune```
+
+Defines :
+1. Public **geneweb.gwb2ged_lib** library (locally - **gwb2ged_lib**).
+ - It includes only one module *Gwb2gedLib*.
+ - Depends on geneweb libraries :
+ - geneweb
+ - gwexport_lib
+2. Public **geneweb.gwb2ge** executable (locally - **gwb2ged**).
+ - It includes only one module *Gwb2ged*.
+ - Depends on geneweb libraries :
+ - geneweb
+ - gwb2ged_lib
+ - gwu_lib
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+ - Depends on extrenal libraries :
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```bin/gwc/dune```
+
+Defines public **geneweb.gwc** executable (locally - **gwc**).
+
+- It includes modules under the `bin/gwc` directory and every of them is preprocessed with ```cppo```.
+- Depends on geneweb libraries :
+ - geneweb
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+- Depends on extrenal libraries :
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```bin/gwd/dune```
+
+Defines:
+
+1. Public wrapped **geneweb.gwd_lib** library (locally - **gwd_lib**).
+ - It includes modules *GwdLog*, *GwdPlugin* and *Request* that are preprocessed with ```cppo```.
+ - Depends on geneweb libraries :
+ - geneweb
+ - wserver
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+2. Public executable **geneweb.gwd** library (locally - **gwd**).
+ - It includes modules *Gwd*, *GwdPluginDep*, *GwdPluginMD5*, *GwdPluginMETA* and *Robot* that are preprocessed with ```cppo```.
+ - Module *GwdPluginMD5* is created by `rule` stanza. Stanza `deps` tells to dune to do firstly:
+ - Find module *Mk_gwdPluginMD5*
+ - Construct all recursively defined in ```plugins/``` aliases `@plugin`.
+ File ```gwdPluginMD5.ml``` is created by running maker script ```mk_gwdPluginMD5.ml``` with path to ```plugins/``` directory specified in argument.
+ - All the libraies used by executable are compiled with `-linkall` flag.
+ - Depends on geneweb libraries :
+ - gwd_lib
+ - geneweb
+ - wserver
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+ - Depends on extrenal libraries :
+ - [dynlink](https://docs.ocaml.pro/docs/META.dynlink@ocamlfind.1.9.1/index.html)
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```bin/gwdiff/dune```
+
+Defines public **geneweb.gwdiff** executable (locally - **gwdiff**).
+
+- It includes only one module *Gwdiff*.
+- Depends on geneweb libraries :
+ - geneweb
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+- Depends on extrenal libraries :
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```bin/gwexport/dune```
+
+Defines public **geneweb.gwexport_lib** library (locally - **gwexport_lib**).
+
+- It includes only one module *Gwexport*.
+- Depends on geneweb libraries :
+ - geneweb
+
+```bin/gwgc/dune```
+
+Defines public **geneweb.gwgc** executable (locally - **gwgc**).
+
+- It includes only one module *Gwgc* that is preprocessed with ```cppo```.
+- Depends on geneweb libraries :
+ - geneweb
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+- Depends on extrenal libraries :
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```bin/gwrepl/dune```
+
+Defines:
+
+1. Private library **gwrepl_deps** that links all dependent libraries.
+ - Depends on geneweb libraries :
+ - geneweb_core
+ - geneweb_def
+ - geneweb_util
+ - geneweb_gwdb
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+ - Depends on external libraries
+ - [stdlib](https://docs.ocaml.pro/docs/LIBRARY.stdlib@ocaml-base-compiler.4.10.0/index.html)
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+2. Public **gwrepl** executable .
+ - It includes only two modules *Gwrepl* and *Data*.
+ - **gwrepl** executable is preprocessed with ```cppo```.
+ - Module *Data* is created with `rule` stanza from :
+ - Module *Mk_data*
+ - File ```.depend``` .
+ File ```data.ml``` is created by running maker script ```mk_data.ml``` that is linked with ```unix.cma``` by OCaml toplevel.
+ - *Data* is preprocessed with [ppx_blob](https://github.com/johnwhitington/ppx_blob) that is used to extract the content of binary file at compile time.
+ - It is compiled by bytecode compiler and producing static object files:
+ ```lisp
+ (modes byte object)
+ ```
+ - All the libraies used by executable are compiled with `-linkall` flag.
+ - Linked with ```-custom``` option that is used by bytecode compiler to produce native executable that embeds the ocamlrun virtual machine as well as the byte code. See [dune-executable](https://dune.readthedocs.io/en/stable/dune-files.html#executable).
+ - Depends on external libraries:
+ - [compiler-libs.toplevel](https://docs.ocaml.pro/docs/META.compiler-libs.toplevel@ocamlfind.1.9.1/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```bin/gwu/dune```
+
+Defines :
+1. Public **geneweb.gwu_lib** library (locally - **gwu_lib**).
+ - It includes only one module *GwuLib*.
+ - Depends on geneweb libraries :
+ - geneweb
+ - gwexport_lib
+2. Public **geneweb.gwu** executable (locally - **gwu**).
+ - It includes only one module *Gwu*.
+ - Depends on geneweb libraries :
+ - geneweb
+ - gwexport_lib
+ - gwu_lib
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+ - Depends on extrenal libraries :
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+```bin/setup/dune```
+
+Defines public **geneweb.setup** executable (locally - **setup**).
+
+- It includes only one module *Setup* that is preprocessed with ```cppo```.
+- Depends on geneweb libraries :
+ - geneweb
+ - wserver
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+- Depends on extrenal libraries :
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+
+```bin/update_nldb/dune```
+
+Defines public **geneweb.update_nldb** executable (locally - **update_nldb**).
+
+- It includes only one module *Update_nldb*.
+- Depends on geneweb libraries :
+ - geneweb
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+- Depends on extrenal libraries :
+ - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+
+```bin/wserver/dune```
+
+Defines public **geneweb.wserver** library (locally - **wserver**).
+
+- It includes only one module *Wserver*.
+- Depends on geneweb libraries :
+ - geneweb_util
+- Depends on extrenal libraries :
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+
+##### Geneweb plugins
+
+```plugins/cgl/dune```
+
+Defines plugin **plugin_cgl**.
+
+- It includes only one module *Plugin_cgl*.
+- Plugin is compiled by native compiler with
+ ```lisp
+ (modes (native plugin))
+ ```
+- Construction of `@plugin` alias depends on plugin's compilation.
+- Depends on geneweb libraries :
+ - geneweb
+ - gwd_lib
+ - wserver
+
+```plugins/export/dune```
+
+Defines plugin **plugin_export**.
+
+- It includes only one module *Plugin_export*.
+- Plugin is compiled by native compiler.
+- Construction of `@plugin` alias depends on plugin's compilation.
+- Compiled with flags that tells to compiler to disable warnings:
+ - Innocuous unused variable.
+ - Constructor or label name used out of scope.
+- Linked statically with libraries :
+ - gwexport_lib
+ - gwu_lib
+ - gwb2ged_lib
+- Depends on geneweb libraries :
+ - geneweb
+ - gwd_lib
+ - gwb2ged_lib
+ - gwexport_lib
+ - gwu_lib
+ - wserver
+
+```plugins/fixbase/dune```
+
+Defines plugin **plugin_fixbase**.
+
+- It includes only one module *Plugin_fixbase*.
+- Plugin is preprocessed with [ppx_deriving.show](https://github.com/ocaml-ppx/ppx_deriving#plugin-show).
+- Plugin is compiled by native compiler with ```-linkall``` option.
+- Construction of `@plugin` alias depends on:
+ - Plugin's compilation
+ - Every file from *assets* directory.
+- Depends on geneweb libraries :
+ - gwd_lib
+ - geneweb_def_show
+
+```plugins/forum/dune```
+
+Defines plugin **plugin_forum**.
+
+- It includes only one module *Plugin_forum* that was obtained by preprocessoing corresponding ```.cppo.ml```. Stanza `rule` in this context is used to remplace lines with directive ```#include``` by one of included file :
+```lisp
+(rule
+ (target plugin_forum.ml)
+ (deps
+ (:included
+ %{project_root}/plugins/forum/forum.ml
+ %{project_root}/plugins/forum/forumDisplay.ml
+ )
+ (:src plugin_forum.cppo.ml)
+ )
+ (action (run %{bin:cppo} %{src} -o %{target}))
+```
+- Plugin is compiled by native compiler.
+- Construction of `@plugin` alias depends on plugin's compilation.
+- Depends on geneweb libraries :
+ - geneweb
+ - gwd_lib
+ - wserver
+
+```plugins/gwxjg/dune```
+
+Defines :
+
+1. Public **geneweb.plugin_gwxjg_lib** library (locally - **plugin_gwxjg_lib**).
+ - It includes modules *Gwxjg_ezgw*, *Gwxjg_data*, *Gwxjg_trans* and *Gwxjg_lexicon_parser*.
+ - Module *Gwxjg_lexicon_parser* is obtained from *gwxjg_lexicon_parser.mll* with stanza `ocamllex` that makes call for [ocamllex](https://ocaml.org/manual/lexyacc.html#s:ocamllex-overview) lexer.
+ - Compiled with flags that tells to compiler to disable warnings:
+ - Constructor or label name used out of scope.
+ - Disambiguated constructor or label name.
+ - Depends on geneweb libraries :
+ - geneweb
+ - Depends on external libraries :
+ - [jingoo](https://docs.ocaml.pro/docs/META.jingoo@jingoo.1.4.3/index.html)
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+2. Plugin **plugin_gwxjg**.
+ - It includes only one module *Plugin_gwxjg*.
+ - Plugin is compiled by native compiler with ```-linkall``` option.
+ - Construction of `@plugin` alias depends on:
+ - Plugin's compilation
+ - *META* file.
+ - Linked statically with libraries :
+ - plugin_gwxjg_lib
+ - Depends on geneweb libraries :
+ - gwd_lib
+ - plugin_gwxjg_lib
+
+```plugins/jingoo/dune```
+
+Defines plugin **plugin_jingoo**.
+
+- It includes only one module *Plugin_jingoo*.
+- Plugin is compiled by native compiler with ```-linkall``` option.
+- Construction of `@plugin` alias depends on plugin's compilation.
+- Linked statically with libraries :
+ - [jingoo](https://docs.ocaml.pro/docs/META.jingoo@jingoo.1.4.3/index.html)
+- Depends on geneweb libraries :
+ - gwd_lib
+
+```plugins/lib_show/dune```
+
+Defines plugin **plugin_lib_show**.
+
+- It includes only one module *Plugin_lib_show*.
+- Plugin is compiled by native compiler with ```-linkall``` option.
+- Construction of `@plugin` alias depends on plugin's compilation.
+- Linked statically with libraries :
+ - geneweb_def_show
+
+```plugins/no_index/dune```
+
+Defines plugin **plugin_no_index**.
+
+- It includes only one module *Plugin_no_index*.
+- Plugin is compiled by native compiler.
+- Construction of `@plugin` alias depends on plugin's compilation.
+- Depends on geneweb libraries :
+ - geneweb
+ - gwd_lib
+ - wserver
+
+```plugins/v7/dune```
+
+1. Wrapped **plugin_v7_lib** library.
+ - It includes modules starting with prefix ```V7```.
+ - Depends on geneweb libraries :
+ - geneweb
+ - geneweb_util
+ - gwd_lib
+2. Plugin **plugin_v7**.
+ - It includes only one module *Plugin_v7*.
+ - Plugin is compiled by native compiler with ```-linkall``` option.
+ - Construction of `@plugin` alias depends on:
+ - Plugin's compilation
+ - Every file from *assets* directory.
+ - Linked statically with libraries :
+ - plugin_v7_lib
+ - Depends on geneweb libraries :
+ - gwd_lib
+ - plugin_v7_lib
+
+```plugins/v7_im/dune```
+
+1. Wrapped **plugin_v7_im_lib** library.
+ - It includes only one module *V7_im_sendImage*.
+ - Depends on geneweb libraries :
+ - geneweb_util
+ - gwd_lib
+2. Plugin **plugin_v7**.
+ - It includes only one module *Plugin_v7_im*.
+ - Plugin is compiled by native compiler.
+ - Construction of `@plugin` alias depends on plugin's compilation.
+ - Linked statically with libraries :
+ - plugin_v7_im_lib
+ - Depends on geneweb libraries :
+ - gwd_lib
+ - plugin_v7_im_lib
+
+```plugins/welcome/dune```
+
+Defines plugin **plugin_welcome**.
+
+- It includes only one module *Plugin_welcome*.
+- Plugin is compiled by native compiler with ```-linkall``` option.
+- Construction of `@plugin` alias depends on:
+ - Plugin's compilation.
+ - Every file from *assets* directory.
+ - *META* file.
+- Depends on geneweb libraries :
+ - gwd_lib
+ - wserver
+ - plugin_v7_lib
+
+##### Geneweb tests
+
+```test/dune```
+
+Defines:
+
+1. **dummy_gwdb** library that is an implementation for **gwdb_driver_mli** virtual library.
+ - It includes only one module *Gwdb_driver*.
+ - Depends on geneweb libraries:
+ - geneweb_def
+ - Depends on external libraries:
+ - [stdlib-shims](https://docs.ocaml.pro/docs/META.stdlib-shims@stdlib-shims.0.3.0/index.html)
+2. **test** executable.
+ - It includes modules starting with prefix ```Test``` that are preprocessed with [ppx_deriving.show](https://github.com/ocaml-ppx/ppx_deriving#plugin-show) PPX rewriter.
+ - Construction of `@runtest` alias depends on execution of **test**.
+ - Depends on geneweb libraries:
+ - geneweb
+ - dummy_gwdb
+ - Chosen implementation of geneweb_sosa_mli.
+ - Depends on external libraries:
+ - [stdlib-shims](https://docs.ocaml.pro/docs/META.stdlib-shims@stdlib-shims.0.3.0/index.html)
+ - [oUnit](https://docs.ocaml.pro/docs/META.oUnit@ounit.2.0.8/index.html)
+
+##### Geneweb benchmarks
+
+```benchmark/dune```
+
+Defines **bench** executable.
+
+- It includes only one module *Bench* that is preprocessed with ```cppo```.
+- Construction of `@runbench` alias depends on execution of **bench**.
+- Depends on geneweb libraries :
+ - geneweb
+ - Chosen implementation of gwdb_driver_mli.
+ - Chosen implementation of geneweb_sosa_mli.
+- Depends on extrenal libraries :
+ - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html)
+ - [benchmark](https://docs.ocaml.pro/docs/META.benchmark@benchmark.1.6/index.html)
diff --git a/doc/html/_sources/dev-doc/overview/database.md.txt b/doc/html/_sources/dev-doc/overview/database.md.txt
new file mode 100644
index 0000000000..afa153d85d
--- /dev/null
+++ b/doc/html/_sources/dev-doc/overview/database.md.txt
@@ -0,0 +1,146 @@
+# Database overview
+
+## Gw files
+
+Genealogy database could be created by Geneweb from one or from multiple source files with *.gw* extension. Those files describe structurally persons, families all kinds of relationships, different events, etc. You can read more about the file structure [here](https://geneweb.tuxfamily.org/wiki/gw). Binary executable `gwc` reads files *.gw*, extracts all persons and families information and passes it to the **Gwdb** module in order to create the database.
+
+## Database entries
+
+Transmitted to **Gwdb** information is composed mainly from:
+
+- Array of all strings that could be any kind of information encoded as a string, like for example: person's name, birth place, marriage place, etc. Identifier `istr` allows to reference the string in the given array (index of an element inside the array).
+
+- Array of persons where each element encompasses information about one person. Every string field of a person (like his name, birthplace, etc.) is an identifier where the real string is stored in array mentioned before. Reference to other persons by means of identifier `iper` that reference person in the current array (index of an element inside the persons array).
+
+- Array of families where each element encompasses information about one family (couple, children, marriage date, etc.). Identifier `ifam` allows to reference the family in the given array (index of element inside the array).
+
+Each array keeps a data structure defined in the module **Def**. Further, those entries will be the main source for every database request.
+
+## Storage
+
+**Gwdb** is responsible for creating the database on the disk from the provided inputs. It creates a directory `dbname.gwb` containing several
+files. The main file `base` contains marshalled representation of each array and `base.acc` stores offsets to every entry entry that allows to make constant time access. Additionally, it creates some index files that associate useful for requests information to the entry's identifier in the `base` file. That helps to requests to find instantly entry without iteration over all existing ones in the database. For example `strings.inx` is a string index that allows to find id for a searched string. One file is slightly different: the `patches` file. It stores every modification done inside the base (see [Modifications](#modifications) subsection). The storage manipulation interface is described in `lib/gwdb_driver.mli/gwdb_driver.mli`. This is a virtual module whose
+current implementation is available on `gwdb-legacy`. Format and description for every database file is listed below:
+
+```text
+base - the base itself
+ magic number (magic_gwb) : string of length 8
+ number of persons : binary_int
+ number of families : binary_int
+ number of strings : binary_int
+ persons array offset in file : binary_int
+ ascends array offset in file : binary_int
+ unions array offset in file : binary_int
+ families array offset in file : binary_int
+ couples array offset in file : binary_int
+ descends array offset in file : binary_int
+ strings array offset in file : binary_int
+ notes origin file : value
+ persons array : value
+ ascends array : value
+ unions array : value
+ families array : value
+ couples array : value
+ descends array : value
+ strings array : value
+
+base.acc - direct accesses to arrays inside base
+ persons offsets : array of binary_ints
+ ascends offsets : array of binary_ints
+ unions offsets : array of binary_ints
+ families offsets : array of binary_ints
+ couples offsets : array of binary_ints
+ descends offsets : array of binary_ints
+ strings offsets : array of binary_ints
+
+names.inx - index for names, strings of first names and surnames
+ offset to sindex : binary_int
+ offset to findex : binary_int
+ 1st index (mixes between names) : value
+ array, length = 16383, associating:
+ - a hash value of a "crushed" (module "Name") name
+ (modulo length)
+ - to the array of ids of the corresponding persons
+ 2nd index (surnames sub-strings) : value
+ array, length = "table_size", associating:
+ - a hash value of the "crushed" (module "Name") surname
+ sub-string (modulo length)
+ - to the array of the corresponding surnnames (string ids)
+ that contain giving surname sub-string
+ 3rd index (first name sub-strings) : value
+ array, length = 16383, associating:
+ - a hash value of the "crushed" (module "Name") first name
+ sub-string (modulo length)
+ - to the array of the corresponding string ids that contains
+ giving first name sub-string
+
+names.acc - direct accesses to values inside arrays in names.inx
+
+strings.inx - index for all strings
+ length of the strings offset array : binary_int
+ strings hash table index : 2 arrays of binary_ints
+ strings offset array (length = prime after 10 * strings
+ array length)
+ - associating a hash value of the string modulo length
+ - to its id in the string array
+ strings list array (length = string array length)
+ - associating a string id
+ - to the id of the next index (previous value) holding the
+ same hash value
+
+snames.inx - index for surnames
+ array ordered by surname
+ - associating the string id of a surname
+ - to a pointer (offset) inside snames.dat
+
+snames.dat - data associated with snames.inx
+ array of list of persons holding a surname
+
+fnames.inx - index for first names
+ array ordered by first name
+ - associating the string id of a first name
+ - to a pointer (offset) inside fnames.dat
+
+fnames.dat - data associated with fnames.inx
+ array of list of persons holding a first name
+
+notes - text file containing data base notes.
+
+notes_d - directory containing .txt for each extended page
+
+particles.txt - text file with autorised name's particles
+
+patches - modification inside the database
+ When updated, none of the previous files are modified.
+ Only this one is written and rewritten. It holds a record
+ of type "patches", composed of association lists
+ "index" - "new value".
+
+nb_persons - number of real persons (with those added by patches)
+
+synchro_patches - timestamped history of base's modifications.
+
+restrict - defines visibility of each person in the base
+
+```
+
+## Modifications
+
+When a modification is requested, geneweb does not update `base` file itself. It
+completes the `patches` file containing all the latest modifications on the
+base. Every modification (patch) done is pended until patches are committed with `commit_patches` request.
+Commit performs update of the `patches` file.
+
+Patching signifies only operations that add or modify an entry. Entry suppression is done quite differently.
+It is replaced by a *dummy* entry and then removed by Geneweb's garbage collector `gwgc` that performs compaction
+of database arrays. Another useful `fixbase` tool, locates and fixes inconsistencies on the base and updates all database files.
+
+## Example
+
+Here is an example how Geneweb displays birth dates of persons that have given name (let's say "Pierre") without considering caches:
+
+- Firstly, it makes dichotomous search inside `fnames.inx` of a string id (`istr`) that references "Pierre"
+- Then it reads (with associated to "Pierre" offset from `fnames.inx`) position in the file `fnames.data` where list of ids of persons (`iper`) with first name "Pierre" are stored.
+- For every person's id it gets person's entry offset from `base.acc` file
+- Then it reads person's entry with giving offset and get field associated to the birth date.
+- Displays all extracted birth dates.
diff --git a/doc/html/_sources/dev-doc/overview/index.rst.txt b/doc/html/_sources/dev-doc/overview/index.rst.txt
new file mode 100644
index 0000000000..7759c4c5fc
--- /dev/null
+++ b/doc/html/_sources/dev-doc/overview/index.rst.txt
@@ -0,0 +1,167 @@
+
+Overview
+========
+
+How to start a Geneweb server
+-----------------------------
+
+Starting the server
+~~~~~~~~~~~~~~~~~~~
+
+Starting a Geneweb web server requires two tools:
+
+- :code:`gwd`, the actual web server;
+- :code:`setup`, the database setup portal.
+
+These two tools are part of the main distribution and can be found on :code:`_build`.
+If you built the project with :code:`make clean distrib`, two scripts are available on
+the directory :code:`distribution/`. Keep in mind they are scripts wrapping the actual
+binaries.
+
+To start the server on the current directory::
+
+ _build/install/default/bin/geneweb.gwd -hd
+
+This will start the main web server on port 2317.
+
+To start the setup server on the current directory::
+
+ _build/default/bin/setup/setup.exe -gd
+
+This will start the setup server on port 2316.
+
+Configuration
+~~~~~~~~~~~~~
+
+Geneweb can be configured by defining a `.gwb` file. An example is
+available in the :code:`etc` directory.
+
+Architecture of Geneweb
+-----------------------
+
+Geneweb is built from three main components:
+
+- the storage of the genealogical trees, divided in a patch file and the actual data;
+- the libraries, reading the data from the storage and writing the patch file
+- the binaries and the web server, reading and writing the storage
+
+.. image:: diagram.png
+
+Storage architecture
+~~~~~~~~~~~~~~~~~~~~
+
+.. toctree::
+ :maxdepth: 2
+
+ database
+
+Libraries
+---------
+
+
+- `Util`: a library with miscellaneous useful modules for manipulating
+ base data types.
+
+- `Def`: the definition of the main type definitions used in Geneweb trees.
+
+- `Sosa`: describes a Sosa-Stradonitz numbering (known an
+ Ahnentafel numbering), associating natural identifiers to individuals. This
+ library is virtual and has three different implementations. One of these
+ implementations is selected by the configuration script.
+
+ * `Sosa_array` is one of the `sosa` implementations. It represents naturals
+ as pair of integers stored in an array `a` such that
+ `sosa = a.(0) + base * a(1)`) where `base` is a hardcoded constant.
+
+ * `Sosa_num` is one of the `sosa` implementation based on the `Big_int`
+ library.
+
+ * `Sosa_zarith` is one of the `sosa` implementation based on the `Zarith`
+ library.
+
+- `Gwdb_driver`: describes the storage implementation. While it is
+ virtual, it currently has only one implementation, `gwdb-legacy`. It is wrapped
+ by the `gwdb` library that exports many tools for database updates.
+
+- `Core`: core of Geneweb for calculating consanguinity between persons.
+
+- `Geneweb_export` : provides a functor for defining Json convertors for Geneweb's
+ datatypes.
+
+- `Def_show`: defines formatters and `string` converters for Geneweb's datatypes.
+
+- `Geneweb`: the main library. It contains several kinds of files, from
+ utilitarian modules to HTML generation.
+
+- `Gwb2gedLib`: defines a function for exporting a base to a GEDCOM file.
+
+- `Wserver`: a light-weight web server, used by `gwd` and `setup`.
+
+- `Gwd_lib`: defines additional modules for the `gwd` web server.
+
+- `GwuLib`: defines useful functions for exporting a database to a `.gw` file.
+
+The documentation of each module is available on the automatically generated
+documentation of geneweb (`$ make doc`)
+
+
+Binaries
+~~~~~~~~
+
+Official binaries
+-----------------
+
+Here are the binaries maintained by Geneweb:
+
+- `Connex <../binaries/connex.html>`_: calculates connex components of a base;
+
+- `Consang <../binaries/consang.html>`_: calculates the consanguinity level of individuals;
+
+- `Ged2gwb <../binaries/ged2gwb.html>`_: imports a GEDCOM 5.5.1 file to a Geneweb base;
+
+- `Gwb2ged <../binaries/gwb2ged.html>`_: exports a base to a GEDCOM 5.5.1 base;
+
+- `Gwc <../binaries/gwc.html>`_: creates a new database;
+
+- `Gwd <../binaries/gwd.html>`_: starts Geneweb's main web server which allows to interact with bases;
+
+- `Gwdiff <../binaries/gwdiff.html>`_: targets differences between two databases;
+
+- `Fixbase <../binaries/gwfixbase.html>`_: checks the consistency of a base and applies patches;
+
+- `Gwgc <../binaries/gwgc.html>`_: removes unused entries in Geneweb's arrays;
+
+- `Gwrepl <../binaries/gwrepl.html>`_: an OCaml interactive top level, useful for scripts;
+
+- `Gwu <../binaries/gwu.html>`_: exports a base to a :code:`.gw` file;
+
+- `Setup <../binaries/setup.html>`_: a web server for selecting and creating databases.
+
+.. toctree::
+ :maxdepth: 1
+ :caption: For mor details about every binary, see:
+
+ ../binaries/official_binaries
+
+Web-server and plug-ins
+-----------------------
+
+The :code:`gwd` web server is customizable with plug-ins; code replacing
+the original behaviour of the web server handling requests. They are
+dynamically loaded by :code:`gwd` at its start and each base can be activated
+through the :code:`.gwb` file (:code:`plugins=*` for activating all plug-ins,
+otherwise :code:`plugins=p1,p2,...`).
+
+A plug-in is composed of its code, a :code:`dune` file for building and a
+:code:`META` file, containing some informations about the plug-in itself.
+
+Here is an example of :code:`META` file:
+
+```
+version:1
+maintainers:OCamlPro
+depends:plugin1,plugin2
+```
+
+The only field taken into account by `gwd` is `depends` as it is used to
+check there are no circular dependencies between plug-ins.
diff --git a/doc/html/_sources/index.rst.txt b/doc/html/_sources/index.rst.txt
new file mode 100644
index 0000000000..828c677972
--- /dev/null
+++ b/doc/html/_sources/index.rst.txt
@@ -0,0 +1,38 @@
+.. audit documentation master file, created by
+ sphinx-quickstart on Mon Nov 22 11:15:19 2021.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to Geneweb Audit's Documentation!
+=========================================
+
+This document is one of the results of OCamlPro's first mission over Geneweb.
+It consists mainly of two parts:
+
+- A developer documentation, complementing the `Geneweb wiki `_.
+ In turn, it provides an overview of the main components of Geneweb, in addition to the documentation for its
+ code part generated using **odoc**.
+
+- An audit that gives recommendations starting from comments on the source code and ending with long term improvement
+ advices. It was based on this `commit `_.
+
+In addition to the document, a `PR `_ makes part of the mission results that, in turn :
+
+* Adds interfaces and documentation to the most of existing modules;
+* Fixes:
+ * CI execution with *appveyor.yml*;
+ * Access to assets files with Secure module.
+
+.. toctree::
+ :maxdepth: 3
+ :caption: Contents:
+
+ dev-doc/index
+ audit/index
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/doc/html/_static/alabaster.css b/doc/html/_static/alabaster.css
new file mode 100644
index 0000000000..0eddaeb07d
--- /dev/null
+++ b/doc/html/_static/alabaster.css
@@ -0,0 +1,701 @@
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: Georgia, serif;
+ font-size: 17px;
+ background-color: #fff;
+ color: #000;
+ margin: 0;
+ padding: 0;
+}
+
+
+div.document {
+ width: 940px;
+ margin: 30px auto 0 auto;
+}
+
+div.documentwrapper {
+ float: left;
+ width: 100%;
+}
+
+div.bodywrapper {
+ margin: 0 0 0 220px;
+}
+
+div.sphinxsidebar {
+ width: 220px;
+ font-size: 14px;
+ line-height: 1.5;
+}
+
+hr {
+ border: 1px solid #B1B4B6;
+}
+
+div.body {
+ background-color: #fff;
+ color: #3E4349;
+ padding: 0 30px 0 30px;
+}
+
+div.body > .section {
+ text-align: left;
+}
+
+div.footer {
+ width: 940px;
+ margin: 20px auto 30px auto;
+ font-size: 14px;
+ color: #888;
+ text-align: right;
+}
+
+div.footer a {
+ color: #888;
+}
+
+p.caption {
+ font-family: inherit;
+ font-size: inherit;
+}
+
+
+div.relations {
+ display: none;
+}
+
+
+div.sphinxsidebar a {
+ color: #444;
+ text-decoration: none;
+ border-bottom: 1px dotted #999;
+}
+
+div.sphinxsidebar a:hover {
+ border-bottom: 1px solid #999;
+}
+
+div.sphinxsidebarwrapper {
+ padding: 18px 10px;
+}
+
+div.sphinxsidebarwrapper p.logo {
+ padding: 0;
+ margin: -10px 0 0 0px;
+ text-align: center;
+}
+
+div.sphinxsidebarwrapper h1.logo {
+ margin-top: -10px;
+ text-align: center;
+ margin-bottom: 5px;
+ text-align: left;
+}
+
+div.sphinxsidebarwrapper h1.logo-name {
+ margin-top: 0px;
+}
+
+div.sphinxsidebarwrapper p.blurb {
+ margin-top: 0;
+ font-style: normal;
+}
+
+div.sphinxsidebar h3,
+div.sphinxsidebar h4 {
+ font-family: Georgia, serif;
+ color: #444;
+ font-size: 24px;
+ font-weight: normal;
+ margin: 0 0 5px 0;
+ padding: 0;
+}
+
+div.sphinxsidebar h4 {
+ font-size: 20px;
+}
+
+div.sphinxsidebar h3 a {
+ color: #444;
+}
+
+div.sphinxsidebar p.logo a,
+div.sphinxsidebar h3 a,
+div.sphinxsidebar p.logo a:hover,
+div.sphinxsidebar h3 a:hover {
+ border: none;
+}
+
+div.sphinxsidebar p {
+ color: #555;
+ margin: 10px 0;
+}
+
+div.sphinxsidebar ul {
+ margin: 10px 0;
+ padding: 0;
+ color: #000;
+}
+
+div.sphinxsidebar ul li.toctree-l1 > a {
+ font-size: 120%;
+}
+
+div.sphinxsidebar ul li.toctree-l2 > a {
+ font-size: 110%;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #CCC;
+ font-family: Georgia, serif;
+ font-size: 1em;
+}
+
+div.sphinxsidebar hr {
+ border: none;
+ height: 1px;
+ color: #AAA;
+ background: #AAA;
+
+ text-align: left;
+ margin-left: 0;
+ width: 50%;
+}
+
+div.sphinxsidebar .badge {
+ border-bottom: none;
+}
+
+div.sphinxsidebar .badge:hover {
+ border-bottom: none;
+}
+
+/* To address an issue with donation coming after search */
+div.sphinxsidebar h3.donation {
+ margin-top: 10px;
+}
+
+/* -- body styles ----------------------------------------------------------- */
+
+a {
+ color: #004B6B;
+ text-decoration: underline;
+}
+
+a:hover {
+ color: #6D4100;
+ text-decoration: underline;
+}
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: Georgia, serif;
+ font-weight: normal;
+ margin: 30px 0px 10px 0px;
+ padding: 0;
+}
+
+div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
+div.body h2 { font-size: 180%; }
+div.body h3 { font-size: 150%; }
+div.body h4 { font-size: 130%; }
+div.body h5 { font-size: 100%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+ color: #DDD;
+ padding: 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ color: #444;
+ background: #EAEAEA;
+}
+
+div.body p, div.body dd, div.body li {
+ line-height: 1.4em;
+}
+
+div.admonition {
+ margin: 20px 0px;
+ padding: 10px 30px;
+ background-color: #EEE;
+ border: 1px solid #CCC;
+}
+
+div.admonition tt.xref, div.admonition code.xref, div.admonition a tt {
+ background-color: #FBFBFB;
+ border-bottom: 1px solid #fafafa;
+}
+
+div.admonition p.admonition-title {
+ font-family: Georgia, serif;
+ font-weight: normal;
+ font-size: 24px;
+ margin: 0 0 10px 0;
+ padding: 0;
+ line-height: 1;
+}
+
+div.admonition p.last {
+ margin-bottom: 0;
+}
+
+div.highlight {
+ background-color: #fff;
+}
+
+dt:target, .highlight {
+ background: #FAF3E8;
+}
+
+div.warning {
+ background-color: #FCC;
+ border: 1px solid #FAA;
+}
+
+div.danger {
+ background-color: #FCC;
+ border: 1px solid #FAA;
+ -moz-box-shadow: 2px 2px 4px #D52C2C;
+ -webkit-box-shadow: 2px 2px 4px #D52C2C;
+ box-shadow: 2px 2px 4px #D52C2C;
+}
+
+div.error {
+ background-color: #FCC;
+ border: 1px solid #FAA;
+ -moz-box-shadow: 2px 2px 4px #D52C2C;
+ -webkit-box-shadow: 2px 2px 4px #D52C2C;
+ box-shadow: 2px 2px 4px #D52C2C;
+}
+
+div.caution {
+ background-color: #FCC;
+ border: 1px solid #FAA;
+}
+
+div.attention {
+ background-color: #FCC;
+ border: 1px solid #FAA;
+}
+
+div.important {
+ background-color: #EEE;
+ border: 1px solid #CCC;
+}
+
+div.note {
+ background-color: #EEE;
+ border: 1px solid #CCC;
+}
+
+div.tip {
+ background-color: #EEE;
+ border: 1px solid #CCC;
+}
+
+div.hint {
+ background-color: #EEE;
+ border: 1px solid #CCC;
+}
+
+div.seealso {
+ background-color: #EEE;
+ border: 1px solid #CCC;
+}
+
+div.topic {
+ background-color: #EEE;
+}
+
+p.admonition-title {
+ display: inline;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre, tt, code {
+ font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
+ font-size: 0.9em;
+}
+
+.hll {
+ background-color: #FFC;
+ margin: 0 -12px;
+ padding: 0 12px;
+ display: block;
+}
+
+img.screenshot {
+}
+
+tt.descname, tt.descclassname, code.descname, code.descclassname {
+ font-size: 0.95em;
+}
+
+tt.descname, code.descname {
+ padding-right: 0.08em;
+}
+
+img.screenshot {
+ -moz-box-shadow: 2px 2px 4px #EEE;
+ -webkit-box-shadow: 2px 2px 4px #EEE;
+ box-shadow: 2px 2px 4px #EEE;
+}
+
+table.docutils {
+ border: 1px solid #888;
+ -moz-box-shadow: 2px 2px 4px #EEE;
+ -webkit-box-shadow: 2px 2px 4px #EEE;
+ box-shadow: 2px 2px 4px #EEE;
+}
+
+table.docutils td, table.docutils th {
+ border: 1px solid #888;
+ padding: 0.25em 0.7em;
+}
+
+table.field-list, table.footnote {
+ border: none;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+table.footnote {
+ margin: 15px 0;
+ width: 100%;
+ border: 1px solid #EEE;
+ background: #FDFDFD;
+ font-size: 0.9em;
+}
+
+table.footnote + table.footnote {
+ margin-top: -15px;
+ border-top: none;
+}
+
+table.field-list th {
+ padding: 0 0.8em 0 0;
+}
+
+table.field-list td {
+ padding: 0;
+}
+
+table.field-list p {
+ margin-bottom: 0.8em;
+}
+
+/* Cloned from
+ * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68
+ */
+.field-name {
+ -moz-hyphens: manual;
+ -ms-hyphens: manual;
+ -webkit-hyphens: manual;
+ hyphens: manual;
+}
+
+table.footnote td.label {
+ width: .1px;
+ padding: 0.3em 0 0.3em 0.5em;
+}
+
+table.footnote td {
+ padding: 0.3em 0.5em;
+}
+
+dl {
+ margin: 0;
+ padding: 0;
+}
+
+dl dd {
+ margin-left: 30px;
+}
+
+blockquote {
+ margin: 0 0 0 30px;
+ padding: 0;
+}
+
+ul, ol {
+ /* Matches the 30px from the narrow-screen "li > ul" selector below */
+ margin: 10px 0 10px 30px;
+ padding: 0;
+}
+
+pre {
+ background: #EEE;
+ padding: 7px 30px;
+ margin: 15px 0px;
+ line-height: 1.3em;
+}
+
+div.viewcode-block:target {
+ background: #ffd;
+}
+
+dl pre, blockquote pre, li pre {
+ margin-left: 0;
+ padding-left: 30px;
+}
+
+tt, code {
+ background-color: #ecf0f3;
+ color: #222;
+ /* padding: 1px 2px; */
+}
+
+tt.xref, code.xref, a tt {
+ background-color: #FBFBFB;
+ border-bottom: 1px solid #fff;
+}
+
+a.reference {
+ text-decoration: none;
+ border-bottom: 1px dotted #004B6B;
+}
+
+/* Don't put an underline on images */
+a.image-reference, a.image-reference:hover {
+ border-bottom: none;
+}
+
+a.reference:hover {
+ border-bottom: 1px solid #6D4100;
+}
+
+a.footnote-reference {
+ text-decoration: none;
+ font-size: 0.7em;
+ vertical-align: top;
+ border-bottom: 1px dotted #004B6B;
+}
+
+a.footnote-reference:hover {
+ border-bottom: 1px solid #6D4100;
+}
+
+a:hover tt, a:hover code {
+ background: #EEE;
+}
+
+
+@media screen and (max-width: 870px) {
+
+ div.sphinxsidebar {
+ display: none;
+ }
+
+ div.document {
+ width: 100%;
+
+ }
+
+ div.documentwrapper {
+ margin-left: 0;
+ margin-top: 0;
+ margin-right: 0;
+ margin-bottom: 0;
+ }
+
+ div.bodywrapper {
+ margin-top: 0;
+ margin-right: 0;
+ margin-bottom: 0;
+ margin-left: 0;
+ }
+
+ ul {
+ margin-left: 0;
+ }
+
+ li > ul {
+ /* Matches the 30px from the "ul, ol" selector above */
+ margin-left: 30px;
+ }
+
+ .document {
+ width: auto;
+ }
+
+ .footer {
+ width: auto;
+ }
+
+ .bodywrapper {
+ margin: 0;
+ }
+
+ .footer {
+ width: auto;
+ }
+
+ .github {
+ display: none;
+ }
+
+
+
+}
+
+
+
+@media screen and (max-width: 875px) {
+
+ body {
+ margin: 0;
+ padding: 20px 30px;
+ }
+
+ div.documentwrapper {
+ float: none;
+ background: #fff;
+ }
+
+ div.sphinxsidebar {
+ display: block;
+ float: none;
+ width: 102.5%;
+ margin: 50px -30px -20px -30px;
+ padding: 10px 20px;
+ background: #333;
+ color: #FFF;
+ }
+
+ div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
+ div.sphinxsidebar h3 a {
+ color: #fff;
+ }
+
+ div.sphinxsidebar a {
+ color: #AAA;
+ }
+
+ div.sphinxsidebar p.logo {
+ display: none;
+ }
+
+ div.document {
+ width: 100%;
+ margin: 0;
+ }
+
+ div.footer {
+ display: none;
+ }
+
+ div.bodywrapper {
+ margin: 0;
+ }
+
+ div.body {
+ min-height: 0;
+ padding: 0;
+ }
+
+ .rtd_doc_footer {
+ display: none;
+ }
+
+ .document {
+ width: auto;
+ }
+
+ .footer {
+ width: auto;
+ }
+
+ .footer {
+ width: auto;
+ }
+
+ .github {
+ display: none;
+ }
+}
+
+
+/* misc. */
+
+.revsys-inline {
+ display: none!important;
+}
+
+/* Make nested-list/multi-paragraph items look better in Releases changelog
+ * pages. Without this, docutils' magical list fuckery causes inconsistent
+ * formatting between different release sub-lists.
+ */
+div#changelog > div.section > ul > li > p:only-child {
+ margin-bottom: 0;
+}
+
+/* Hide fugly table cell borders in ..bibliography:: directive output */
+table.docutils.citation, table.docutils.citation td, table.docutils.citation th {
+ border: none;
+ /* Below needed in some edge cases; if not applied, bottom shadows appear */
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+
+/* relbar */
+
+.related {
+ line-height: 30px;
+ width: 100%;
+ font-size: 0.9rem;
+}
+
+.related.top {
+ border-bottom: 1px solid #EEE;
+ margin-bottom: 20px;
+}
+
+.related.bottom {
+ border-top: 1px solid #EEE;
+}
+
+.related ul {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+}
+
+.related li {
+ display: inline;
+}
+
+nav#rellinks {
+ float: right;
+}
+
+nav#rellinks li+li:before {
+ content: "|";
+}
+
+nav#breadcrumbs li+li:before {
+ content: "\00BB";
+}
+
+/* Hide certain items when printing */
+@media print {
+ div.related {
+ display: none;
+ }
+}
\ No newline at end of file
diff --git a/doc/html/_static/basic.css b/doc/html/_static/basic.css
new file mode 100644
index 0000000000..be19270e4a
--- /dev/null
+++ b/doc/html/_static/basic.css
@@ -0,0 +1,856 @@
+/*
+ * basic.css
+ * ~~~~~~~~~
+ *
+ * Sphinx stylesheet -- basic theme.
+ *
+ * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/* -- main layout ----------------------------------------------------------- */
+
+div.clearer {
+ clear: both;
+}
+
+div.section::after {
+ display: block;
+ content: '';
+ clear: left;
+}
+
+/* -- relbar ---------------------------------------------------------------- */
+
+div.related {
+ width: 100%;
+ font-size: 90%;
+}
+
+div.related h3 {
+ display: none;
+}
+
+div.related ul {
+ margin: 0;
+ padding: 0 0 0 10px;
+ list-style: none;
+}
+
+div.related li {
+ display: inline;
+}
+
+div.related li.right {
+ float: right;
+ margin-right: 5px;
+}
+
+/* -- sidebar --------------------------------------------------------------- */
+
+div.sphinxsidebarwrapper {
+ padding: 10px 5px 0 10px;
+}
+
+div.sphinxsidebar {
+ float: left;
+ width: 230px;
+ margin-left: -100%;
+ font-size: 90%;
+ word-wrap: break-word;
+ overflow-wrap : break-word;
+}
+
+div.sphinxsidebar ul {
+ list-style: none;
+}
+
+div.sphinxsidebar ul ul,
+div.sphinxsidebar ul.want-points {
+ margin-left: 20px;
+ list-style: square;
+}
+
+div.sphinxsidebar ul ul {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+div.sphinxsidebar form {
+ margin-top: 10px;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #98dbcc;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+div.sphinxsidebar #searchbox form.search {
+ overflow: hidden;
+}
+
+div.sphinxsidebar #searchbox input[type="text"] {
+ float: left;
+ width: 80%;
+ padding: 0.25em;
+ box-sizing: border-box;
+}
+
+div.sphinxsidebar #searchbox input[type="submit"] {
+ float: left;
+ width: 20%;
+ border-left: none;
+ padding: 0.25em;
+ box-sizing: border-box;
+}
+
+
+img {
+ border: 0;
+ max-width: 100%;
+}
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+ margin: 10px 0 0 20px;
+ padding: 0;
+}
+
+ul.search li {
+ padding: 5px 0 5px 20px;
+ background-image: url(file.png);
+ background-repeat: no-repeat;
+ background-position: 0 7px;
+}
+
+ul.search li a {
+ font-weight: bold;
+}
+
+ul.search li div.context {
+ color: #888;
+ margin: 2px 0 0 30px;
+ text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+ font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+ width: 90%;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.contentstable p.biglink {
+ line-height: 150%;
+}
+
+a.biglink {
+ font-size: 1.3em;
+}
+
+span.linkdescr {
+ font-style: italic;
+ padding-top: 5px;
+ font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable {
+ width: 100%;
+}
+
+table.indextable td {
+ text-align: left;
+ vertical-align: top;
+}
+
+table.indextable ul {
+ margin-top: 0;
+ margin-bottom: 0;
+ list-style-type: none;
+}
+
+table.indextable > tbody > tr > td > ul {
+ padding-left: 0em;
+}
+
+table.indextable tr.pcap {
+ height: 10px;
+}
+
+table.indextable tr.cap {
+ margin-top: 10px;
+ background-color: #f2f2f2;
+}
+
+img.toggler {
+ margin-right: 3px;
+ margin-top: 3px;
+ cursor: pointer;
+}
+
+div.modindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+div.genindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+/* -- domain module index --------------------------------------------------- */
+
+table.modindextable td {
+ padding: 2px;
+ border-collapse: collapse;
+}
+
+/* -- general body styles --------------------------------------------------- */
+
+div.body {
+ min-width: 450px;
+ max-width: 800px;
+}
+
+div.body p, div.body dd, div.body li, div.body blockquote {
+ -moz-hyphens: auto;
+ -ms-hyphens: auto;
+ -webkit-hyphens: auto;
+ hyphens: auto;
+}
+
+a.headerlink {
+ visibility: hidden;
+}
+
+a.brackets:before,
+span.brackets > a:before{
+ content: "[";
+}
+
+a.brackets:after,
+span.brackets > a:after {
+ content: "]";
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink,
+caption:hover > a.headerlink,
+p.caption:hover > a.headerlink,
+div.code-block-caption:hover > a.headerlink {
+ visibility: visible;
+}
+
+div.body p.caption {
+ text-align: inherit;
+}
+
+div.body td {
+ text-align: left;
+}
+
+.first {
+ margin-top: 0 !important;
+}
+
+p.rubric {
+ margin-top: 30px;
+ font-weight: bold;
+}
+
+img.align-left, .figure.align-left, object.align-left {
+ clear: left;
+ float: left;
+ margin-right: 1em;
+}
+
+img.align-right, .figure.align-right, object.align-right {
+ clear: right;
+ float: right;
+ margin-left: 1em;
+}
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+img.align-default, .figure.align-default {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left;
+}
+
+.align-center {
+ text-align: center;
+}
+
+.align-default {
+ text-align: center;
+}
+
+.align-right {
+ text-align: right;
+}
+
+/* -- sidebars -------------------------------------------------------------- */
+
+div.sidebar {
+ margin: 0 0 0.5em 1em;
+ border: 1px solid #ddb;
+ padding: 7px;
+ background-color: #ffe;
+ width: 40%;
+ float: right;
+ clear: right;
+ overflow-x: auto;
+}
+
+p.sidebar-title {
+ font-weight: bold;
+}
+
+div.admonition, div.topic, blockquote {
+ clear: left;
+}
+
+/* -- topics ---------------------------------------------------------------- */
+
+div.topic {
+ border: 1px solid #ccc;
+ padding: 7px;
+ margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+/* -- admonitions ----------------------------------------------------------- */
+
+div.admonition {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding: 7px;
+}
+
+div.admonition dt {
+ font-weight: bold;
+}
+
+p.admonition-title {
+ margin: 0px 10px 5px 0px;
+ font-weight: bold;
+}
+
+div.body p.centered {
+ text-align: center;
+ margin-top: 25px;
+}
+
+/* -- content of sidebars/topics/admonitions -------------------------------- */
+
+div.sidebar > :last-child,
+div.topic > :last-child,
+div.admonition > :last-child {
+ margin-bottom: 0;
+}
+
+div.sidebar::after,
+div.topic::after,
+div.admonition::after,
+blockquote::after {
+ display: block;
+ content: '';
+ clear: both;
+}
+
+/* -- tables ---------------------------------------------------------------- */
+
+table.docutils {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ border: 0;
+ border-collapse: collapse;
+}
+
+table.align-center {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.align-default {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table caption span.caption-number {
+ font-style: italic;
+}
+
+table caption span.caption-text {
+}
+
+table.docutils td, table.docutils th {
+ padding: 1px 8px 1px 5px;
+ border-top: 0;
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 1px solid #aaa;
+}
+
+table.footnote td, table.footnote th {
+ border: 0 !important;
+}
+
+th {
+ text-align: left;
+ padding-right: 5px;
+}
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px;
+}
+
+table.citation td {
+ border-bottom: none;
+}
+
+th > :first-child,
+td > :first-child {
+ margin-top: 0px;
+}
+
+th > :last-child,
+td > :last-child {
+ margin-bottom: 0px;
+}
+
+/* -- figures --------------------------------------------------------------- */
+
+div.figure {
+ margin: 0.5em;
+ padding: 0.5em;
+}
+
+div.figure p.caption {
+ padding: 0.3em;
+}
+
+div.figure p.caption span.caption-number {
+ font-style: italic;
+}
+
+div.figure p.caption span.caption-text {
+}
+
+/* -- field list styles ----------------------------------------------------- */
+
+table.field-list td, table.field-list th {
+ border: 0 !important;
+}
+
+.field-list ul {
+ margin: 0;
+ padding-left: 1em;
+}
+
+.field-list p {
+ margin: 0;
+}
+
+.field-name {
+ -moz-hyphens: manual;
+ -ms-hyphens: manual;
+ -webkit-hyphens: manual;
+ hyphens: manual;
+}
+
+/* -- hlist styles ---------------------------------------------------------- */
+
+table.hlist {
+ margin: 1em 0;
+}
+
+table.hlist td {
+ vertical-align: top;
+}
+
+
+/* -- other body styles ----------------------------------------------------- */
+
+ol.arabic {
+ list-style: decimal;
+}
+
+ol.loweralpha {
+ list-style: lower-alpha;
+}
+
+ol.upperalpha {
+ list-style: upper-alpha;
+}
+
+ol.lowerroman {
+ list-style: lower-roman;
+}
+
+ol.upperroman {
+ list-style: upper-roman;
+}
+
+:not(li) > ol > li:first-child > :first-child,
+:not(li) > ul > li:first-child > :first-child {
+ margin-top: 0px;
+}
+
+:not(li) > ol > li:last-child > :last-child,
+:not(li) > ul > li:last-child > :last-child {
+ margin-bottom: 0px;
+}
+
+ol.simple ol p,
+ol.simple ul p,
+ul.simple ol p,
+ul.simple ul p {
+ margin-top: 0;
+}
+
+ol.simple > li:not(:first-child) > p,
+ul.simple > li:not(:first-child) > p {
+ margin-top: 0;
+}
+
+ol.simple p,
+ul.simple p {
+ margin-bottom: 0;
+}
+
+dl.footnote > dt,
+dl.citation > dt {
+ float: left;
+ margin-right: 0.5em;
+}
+
+dl.footnote > dd,
+dl.citation > dd {
+ margin-bottom: 0em;
+}
+
+dl.footnote > dd:after,
+dl.citation > dd:after {
+ content: "";
+ clear: both;
+}
+
+dl.field-list {
+ display: grid;
+ grid-template-columns: fit-content(30%) auto;
+}
+
+dl.field-list > dt {
+ font-weight: bold;
+ word-break: break-word;
+ padding-left: 0.5em;
+ padding-right: 5px;
+}
+
+dl.field-list > dt:after {
+ content: ":";
+}
+
+dl.field-list > dd {
+ padding-left: 0.5em;
+ margin-top: 0em;
+ margin-left: 0em;
+ margin-bottom: 0em;
+}
+
+dl {
+ margin-bottom: 15px;
+}
+
+dd > :first-child {
+ margin-top: 0px;
+}
+
+dd ul, dd table {
+ margin-bottom: 10px;
+}
+
+dd {
+ margin-top: 3px;
+ margin-bottom: 10px;
+ margin-left: 30px;
+}
+
+dl > dd:last-child,
+dl > dd:last-child > :last-child {
+ margin-bottom: 0;
+}
+
+dt:target, span.highlighted {
+ background-color: #fbe54e;
+}
+
+rect.highlighted {
+ fill: #fbe54e;
+}
+
+dl.glossary dt {
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
+.optional {
+ font-size: 1.3em;
+}
+
+.sig-paren {
+ font-size: larger;
+}
+
+.versionmodified {
+ font-style: italic;
+}
+
+.system-message {
+ background-color: #fda;
+ padding: 5px;
+ border: 3px solid red;
+}
+
+.footnote:target {
+ background-color: #ffa;
+}
+
+.line-block {
+ display: block;
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+.line-block .line-block {
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-left: 1.5em;
+}
+
+.guilabel, .menuselection {
+ font-family: sans-serif;
+}
+
+.accelerator {
+ text-decoration: underline;
+}
+
+.classifier {
+ font-style: oblique;
+}
+
+.classifier:before {
+ font-style: normal;
+ margin: 0.5em;
+ content: ":";
+}
+
+abbr, acronym {
+ border-bottom: dotted 1px;
+ cursor: help;
+}
+
+/* -- code displays --------------------------------------------------------- */
+
+pre {
+ overflow: auto;
+ overflow-y: hidden; /* fixes display issues on Chrome browsers */
+}
+
+pre, div[class*="highlight-"] {
+ clear: both;
+}
+
+span.pre {
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ -webkit-hyphens: none;
+ hyphens: none;
+}
+
+div[class*="highlight-"] {
+ margin: 1em 0;
+}
+
+td.linenos pre {
+ border: 0;
+ background-color: transparent;
+ color: #aaa;
+}
+
+table.highlighttable {
+ display: block;
+}
+
+table.highlighttable tbody {
+ display: block;
+}
+
+table.highlighttable tr {
+ display: flex;
+}
+
+table.highlighttable td {
+ margin: 0;
+ padding: 0;
+}
+
+table.highlighttable td.linenos {
+ padding-right: 0.5em;
+}
+
+table.highlighttable td.code {
+ flex: 1;
+ overflow: hidden;
+}
+
+.highlight .hll {
+ display: block;
+}
+
+div.highlight pre,
+table.highlighttable pre {
+ margin: 0;
+}
+
+div.code-block-caption + div {
+ margin-top: 0;
+}
+
+div.code-block-caption {
+ margin-top: 1em;
+ padding: 2px 5px;
+ font-size: small;
+}
+
+div.code-block-caption code {
+ background-color: transparent;
+}
+
+table.highlighttable td.linenos,
+span.linenos,
+div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */
+ user-select: none;
+}
+
+div.code-block-caption span.caption-number {
+ padding: 0.1em 0.3em;
+ font-style: italic;
+}
+
+div.code-block-caption span.caption-text {
+}
+
+div.literal-block-wrapper {
+ margin: 1em 0;
+}
+
+code.descname {
+ background-color: transparent;
+ font-weight: bold;
+ font-size: 1.2em;
+}
+
+code.descclassname {
+ background-color: transparent;
+}
+
+code.xref, a code {
+ background-color: transparent;
+ font-weight: bold;
+}
+
+h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
+ background-color: transparent;
+}
+
+.viewcode-link {
+ float: right;
+}
+
+.viewcode-back {
+ float: right;
+ font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+ margin: -1px -10px;
+ padding: 0 10px;
+}
+
+/* -- math display ---------------------------------------------------------- */
+
+img.math {
+ vertical-align: middle;
+}
+
+div.body div.math p {
+ text-align: center;
+}
+
+span.eqno {
+ float: right;
+}
+
+span.eqno a.headerlink {
+ position: absolute;
+ z-index: 1;
+}
+
+div.math:hover a.headerlink {
+ visibility: visible;
+}
+
+/* -- printout stylesheet --------------------------------------------------- */
+
+@media print {
+ div.document,
+ div.documentwrapper,
+ div.bodywrapper {
+ margin: 0 !important;
+ width: 100%;
+ }
+
+ div.sphinxsidebar,
+ div.related,
+ div.footer,
+ #top-link {
+ display: none;
+ }
+}
\ No newline at end of file
diff --git a/doc/html/_static/custom.css b/doc/html/_static/custom.css
new file mode 100644
index 0000000000..2a924f1d6a
--- /dev/null
+++ b/doc/html/_static/custom.css
@@ -0,0 +1 @@
+/* This file intentionally left blank. */
diff --git a/doc/html/_static/doctools.js b/doc/html/_static/doctools.js
new file mode 100644
index 0000000000..61ac9d266f
--- /dev/null
+++ b/doc/html/_static/doctools.js
@@ -0,0 +1,321 @@
+/*
+ * doctools.js
+ * ~~~~~~~~~~~
+ *
+ * Sphinx JavaScript utilities for all documentation.
+ *
+ * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/**
+ * select a different prefix for underscore
+ */
+$u = _.noConflict();
+
+/**
+ * make the code below compatible with browsers without
+ * an installed firebug like debugger
+if (!window.console || !console.firebug) {
+ var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
+ "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
+ "profile", "profileEnd"];
+ window.console = {};
+ for (var i = 0; i < names.length; ++i)
+ window.console[names[i]] = function() {};
+}
+ */
+
+/**
+ * small helper function to urldecode strings
+ *
+ * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
+ */
+jQuery.urldecode = function(x) {
+ if (!x) {
+ return x
+ }
+ return decodeURIComponent(x.replace(/\+/g, ' '));
+};
+
+/**
+ * small helper function to urlencode strings
+ */
+jQuery.urlencode = encodeURIComponent;
+
+/**
+ * This function returns the parsed url parameters of the
+ * current request. Multiple values per key are supported,
+ * it will always return arrays of strings for the value parts.
+ */
+jQuery.getQueryParameters = function(s) {
+ if (typeof s === 'undefined')
+ s = document.location.search;
+ var parts = s.substr(s.indexOf('?') + 1).split('&');
+ var result = {};
+ for (var i = 0; i < parts.length; i++) {
+ var tmp = parts[i].split('=', 2);
+ var key = jQuery.urldecode(tmp[0]);
+ var value = jQuery.urldecode(tmp[1]);
+ if (key in result)
+ result[key].push(value);
+ else
+ result[key] = [value];
+ }
+ return result;
+};
+
+/**
+ * highlight a given string on a jquery object by wrapping it in
+ * span elements with the given class name.
+ */
+jQuery.fn.highlightText = function(text, className) {
+ function highlight(node, addItems) {
+ if (node.nodeType === 3) {
+ var val = node.nodeValue;
+ var pos = val.toLowerCase().indexOf(text);
+ if (pos >= 0 &&
+ !jQuery(node.parentNode).hasClass(className) &&
+ !jQuery(node.parentNode).hasClass("nohighlight")) {
+ var span;
+ var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
+ if (isInSVG) {
+ span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
+ } else {
+ span = document.createElement("span");
+ span.className = className;
+ }
+ span.appendChild(document.createTextNode(val.substr(pos, text.length)));
+ node.parentNode.insertBefore(span, node.parentNode.insertBefore(
+ document.createTextNode(val.substr(pos + text.length)),
+ node.nextSibling));
+ node.nodeValue = val.substr(0, pos);
+ if (isInSVG) {
+ var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
+ var bbox = node.parentElement.getBBox();
+ rect.x.baseVal.value = bbox.x;
+ rect.y.baseVal.value = bbox.y;
+ rect.width.baseVal.value = bbox.width;
+ rect.height.baseVal.value = bbox.height;
+ rect.setAttribute('class', className);
+ addItems.push({
+ "parent": node.parentNode,
+ "target": rect});
+ }
+ }
+ }
+ else if (!jQuery(node).is("button, select, textarea")) {
+ jQuery.each(node.childNodes, function() {
+ highlight(this, addItems);
+ });
+ }
+ }
+ var addItems = [];
+ var result = this.each(function() {
+ highlight(this, addItems);
+ });
+ for (var i = 0; i < addItems.length; ++i) {
+ jQuery(addItems[i].parent).before(addItems[i].target);
+ }
+ return result;
+};
+
+/*
+ * backward compatibility for jQuery.browser
+ * This will be supported until firefox bug is fixed.
+ */
+if (!jQuery.browser) {
+ jQuery.uaMatch = function(ua) {
+ ua = ua.toLowerCase();
+
+ var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
+ /(webkit)[ \/]([\w.]+)/.exec(ua) ||
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
+ /(msie) ([\w.]+)/.exec(ua) ||
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
+ [];
+
+ return {
+ browser: match[ 1 ] || "",
+ version: match[ 2 ] || "0"
+ };
+ };
+ jQuery.browser = {};
+ jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
+}
+
+/**
+ * Small JavaScript module for the documentation.
+ */
+var Documentation = {
+
+ init : function() {
+ this.fixFirefoxAnchorBug();
+ this.highlightSearchWords();
+ this.initIndexTable();
+ if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) {
+ this.initOnKeyListeners();
+ }
+ },
+
+ /**
+ * i18n support
+ */
+ TRANSLATIONS : {},
+ PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; },
+ LOCALE : 'unknown',
+
+ // gettext and ngettext don't access this so that the functions
+ // can safely bound to a different name (_ = Documentation.gettext)
+ gettext : function(string) {
+ var translated = Documentation.TRANSLATIONS[string];
+ if (typeof translated === 'undefined')
+ return string;
+ return (typeof translated === 'string') ? translated : translated[0];
+ },
+
+ ngettext : function(singular, plural, n) {
+ var translated = Documentation.TRANSLATIONS[singular];
+ if (typeof translated === 'undefined')
+ return (n == 1) ? singular : plural;
+ return translated[Documentation.PLURALEXPR(n)];
+ },
+
+ addTranslations : function(catalog) {
+ for (var key in catalog.messages)
+ this.TRANSLATIONS[key] = catalog.messages[key];
+ this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
+ this.LOCALE = catalog.locale;
+ },
+
+ /**
+ * add context elements like header anchor links
+ */
+ addContextElements : function() {
+ $('div[id] > :header:first').each(function() {
+ $('\u00B6').
+ attr('href', '#' + this.id).
+ attr('title', _('Permalink to this headline')).
+ appendTo(this);
+ });
+ $('dt[id]').each(function() {
+ $('\u00B6').
+ attr('href', '#' + this.id).
+ attr('title', _('Permalink to this definition')).
+ appendTo(this);
+ });
+ },
+
+ /**
+ * workaround a firefox stupidity
+ * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
+ */
+ fixFirefoxAnchorBug : function() {
+ if (document.location.hash && $.browser.mozilla)
+ window.setTimeout(function() {
+ document.location.href += '';
+ }, 10);
+ },
+
+ /**
+ * highlight the search words provided in the url in the text
+ */
+ highlightSearchWords : function() {
+ var params = $.getQueryParameters();
+ var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
+ if (terms.length) {
+ var body = $('div.body');
+ if (!body.length) {
+ body = $('body');
+ }
+ window.setTimeout(function() {
+ $.each(terms, function() {
+ body.highlightText(this.toLowerCase(), 'highlighted');
+ });
+ }, 10);
+ $('