Files
Geneweb/bin/gwc/db1link.ml
2024-03-05 22:01:20 +01:00

1703 lines
59 KiB
OCaml

(* Copyright (c) 1998-2007 INRIA *)
open Geneweb
open Gwcomp
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
type person = (int, int, int) Def.gen_person
(** Extended person's entry in the base *)
type ascend = int Def.gen_ascend
(** Person's ascendants entry in the base *)
type union = int Def.gen_union
(** Person's union entry in the base *)
type family = (int, int, int) Def.gen_family
(** Family's entry in the base *)
type couple = int Def.gen_couple
(** Family's couple entry in the base *)
type descend = int Def.gen_descend
(** Family's descendants entry in the base *)
type ('person, 'string) gen_min_person = {
mutable m_first_name : 'string;
mutable m_surname : 'string;
mutable m_occ : int;
mutable m_rparents : ('person, 'string) gen_relation list;
mutable m_related : int list;
mutable m_pevents : ('person, 'string) gen_pers_event list;
mutable m_sex : sex;
mutable m_notes : 'string;
}
(** Restricted to the minimum [Def.gen_person] data type. *)
type min_person = (int, int) gen_min_person
(** Person's entry in the base *)
type cbase = {
(* 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;
}
(** State of the base collecting all information at link time used to
create further Geneweb database *)
type file_info = {
(* current .gw filename *)
mutable f_curr_src_file : string; (* current .gwo filename *)
mutable f_curr_gwo_file : string;
(* 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;
}
(** Information about current .gwo file. *)
type gen = {
(* 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;
}
(** Global linker state *)
(** 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 *)
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
Array.blit arr 0 new_arr 0 (Array.length arr);
gen.g_base.c_strings <- new_arr);
let u = gen.g_scnt in
gen.g_base.c_strings.(gen.g_scnt) <- x;
gen.g_scnt <- gen.g_scnt + 1;
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 =
{
marriage = Date.cdate_None;
marriage_place = empty_string;
marriage_note = empty_string;
marriage_src = empty_string;
witnesses = [||];
relation = NoMention;
divorce = NotDivorced;
fevents = [];
comment = empty_string;
origin_file = empty_string;
fsources = empty_string;
fam_index = -1;
}
in
let cpl = Adef.couple 0 0 in
let des = { children = [||] } in
(fam, cpl, des)
(** 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;
m_occ = occ;
m_rparents = [];
m_related = [];
m_pevents = [];
m_sex = Neuter;
m_notes = empty_string;
}
and a = { parents = None; consang = Adef.fix (-1) }
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
let asc_arr = gen.g_base.c_ascends in
let uni_arr = gen.g_base.c_unions in
let new_size = (2 * Array.length per_arr) + 1 in
let phony_per, phony_asc, phony_uni = no_person gen in
let new_per_arr = Array.make new_size phony_per in
let new_asc_arr = Array.make new_size phony_asc in
let new_uni_arr = Array.make new_size phony_uni in
let new_def = Array.make new_size false in
Array.blit per_arr 0 new_per_arr 0 (Array.length per_arr);
gen.g_base.c_persons <- new_per_arr;
Array.blit asc_arr 0 new_asc_arr 0 (Array.length asc_arr);
gen.g_base.c_ascends <- new_asc_arr;
Array.blit uni_arr 0 new_uni_arr 0 (Array.length uni_arr);
gen.g_base.c_unions <- new_uni_arr;
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
let cpl_arr = gen.g_base.c_couples in
let des_arr = gen.g_base.c_descends in
let new_size = (2 * Array.length cpl_arr) + 1 in
let phony_fam, phony_cpl, phony_des = no_family gen in
let new_fam_arr = Array.make new_size phony_fam in
let new_cpl_arr = Array.make new_size phony_cpl in
let new_des_arr = Array.make new_size phony_des in
Array.blit fam_arr 0 new_fam_arr 0 (Array.length fam_arr);
gen.g_base.c_families <- new_fam_arr;
Array.blit cpl_arr 0 new_cpl_arr 0 (Array.length cpl_arr);
gen.g_base.c_couples <- new_cpl_arr;
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
let s = Name.crush_lower (first_name ^ " " ^ surname) in
let key = Hashtbl.hash s in
let ipl = Hashtbl.find_all gen.g_names key in
let first_name = Name.lower first_name in
let surname = Name.lower surname in
let rec loop = function
| [] -> 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
then ip
else loop ipl
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
let s = Name.crush_lower (first_name ^ " " ^ surname) in
let key = Hashtbl.hash s in
let ipl = Hashtbl.find_all gen.g_file_info.f_local_names (key, occ) in
let first_name = Name.lower first_name in
let surname = Name.lower surname in
let rec loop = function
| [] -> 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 ip
else loop ipl
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 Not_found -> occ
in
let rec loop occ =
match
try Some (find_person_by_global_name gen fn sn occ)
with Not_found -> None
with
| Some _ -> loop (occ + 1)
| None ->
Hashtbl.add gen.g_first_av_occ (fn, sn) occ;
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
else
let ip = 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 ->
(* available occurence number *)
let new_occ =
if
gen.g_file_info.f_separate && key.pk_first_name <> "?"
&& key.pk_surname <> "?"
then 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
let h = person_hash key.pk_first_name key.pk_surname in
Hashtbl.add gen.g_file_info.f_local_names (h, occ) i);
(* 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
if not gen.g_errored then
if
sou gen.g_base x.m_first_name <> key.pk_first_name
|| sou gen.g_base x.m_surname <> key.pk_surname
then (
Printf.printf "\nPerson defined with two spellings:\n";
Printf.printf " \"%s%s %s\"\n" key.pk_first_name
(match x.m_occ with 0 -> "" | n -> "." ^ string_of_int n)
key.pk_surname;
Printf.printf " \"%s%s %s\"\n"
(p_first_name gen.g_base x)
(match occ with 0 -> "" | n -> "." ^ string_of_int n)
(p_surname gen.g_base x);
gen.g_def.(ip) <- true;
check_error gen);
(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 ->
(* available occurence number *)
let new_occ =
if
gen.g_file_info.f_separate && so.first_name <> "?"
&& so.surname <> "?"
then 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
let h = person_hash so.first_name so.surname in
Hashtbl.add gen.g_file_info.f_local_names (h, occ) i);
(x, i)
in
(* if person wad defined before (not just referenced) *)
if gen.g_def.(ip) then (
(* 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 -> "" | n -> "." ^ string_of_int n)
so.surname;
if
p_first_name gen.g_base x <> so.first_name
|| p_surname gen.g_base x <> so.surname
then
Printf.printf "as name: \"%s%s %s\"\n"
(p_first_name gen.g_base x)
(match occ with 0 -> "" | n -> "." ^ string_of_int n)
(p_surname gen.g_base x);
flush stdout;
check_error gen)
else (* else set it as defined *)
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 *)
Printf.printf "\nPerson defined with two spellings:\n";
Printf.printf " \"%s%s %s\"\n" so.first_name
(match x.m_occ with 0 -> "" | n -> "." ^ string_of_int n)
so.surname;
Printf.printf " \"%s%s %s\"\n"
(p_first_name gen.g_base x)
(match occ with 0 -> "" | n -> "." ^ string_of_int n)
(p_surname gen.g_base x);
gen.g_def.(ip) <- true;
check_error gen);
if not gen.g_errored then (
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;
first_names_aliases =
List.map (unique_string gen) so.first_names_aliases;
surnames_aliases = List.map (unique_string gen) so.surnames_aliases;
public_name = unique_string gen so.public_name;
qualifiers = List.map (unique_string gen) so.qualifiers;
aliases = List.map (unique_string gen) so.aliases;
titles = List.map (title_unique_string gen) so.titles;
rparents = [];
related = [];
occupation = unique_string gen so.occupation;
sex = Neuter;
access = so.access;
birth = so.birth;
birth_place = unique_string gen so.birth_place;
birth_note = unique_string gen so.birth_note;
birth_src = unique_string gen so.birth_src;
baptism = so.baptism;
baptism_place = unique_string gen so.baptism_place;
baptism_note = unique_string gen so.baptism_note;
baptism_src = unique_string gen so.baptism_src;
death = so.death;
death_place = unique_string gen so.death_place;
death_note = unique_string gen so.death_note;
death_src = unique_string gen so.death_src;
burial = so.burial;
burial_place = unique_string gen so.burial_place;
burial_note = unique_string gen so.burial_note;
burial_src = unique_string gen so.burial_src;
pevents = [];
notes = empty_string;
psources =
unique_string gen
(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));
(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 children [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
| Some int ->
let cpl = coi gen.g_base int in
let p = Adef.father cpl in
let m = Adef.mother cpl in
Printf.printf
"I cannot add \"%s\", child of\n\
\ - \"%s\"\n\
\ - \"%s\",\n\
because this persons still exists as child of\n\
\ - \"%s\"\n\
\ - \"%s\"." (designation gen.g_base x)
(designation gen.g_base fath)
(designation gen.g_base moth)
(designation gen.g_base (poi gen.g_base p))
(designation gen.g_base (poi gen.g_base m));
flush stdout;
(*
x.birth := Adef.cdate_None;
x.death := DontKnowIfDead;
*)
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
| Efam_Divorce | Efam_Separated | Efam_Annulation | Efam_MarriageBann
| Efam_MarriageContract | Efam_MarriageLicense | Efam_PACS | Efam_Residence
) as evt ->
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
let nsck_std_fields =
match fam.relation with
| NoSexesCheckNotMarried | NoSexesCheckMarried -> true
| _ -> false
in
(* On veut cette fois ci que ce soit le dernier évènement *)
(* qui soit mis dans les évènements principaux. *)
let convert relation =
match relation with
| Efam_Marriage ->
if nsck_std_fields then Some NoSexesCheckMarried else Some Married
| Efam_NoMarriage ->
if nsck_std_fields then Some NoSexesCheckNotMarried else Some NotMarried
| Efam_Engage -> Some Engaged
| Efam_NoMention -> Some NoMention
| Efam_MarriageBann -> Some MarriageBann
| Efam_MarriageContract -> Some MarriageContract
| Efam_MarriageLicense -> Some MarriageLicense
| Efam_PACS -> Some Pacs
| Efam_Residence -> Some Residence
| _ -> None
in
let rec loop fevents fam =
match fevents with
| [] -> fam
| evt :: l -> (
match convert evt.efam_name with
| Some relation' ->
if !found_marriage then loop l fam
else
let witnesses = Array.map fst evt.efam_witnesses in
let fam =
{
fam with
relation = relation';
marriage = evt.efam_date;
marriage_place = evt.efam_place;
marriage_note = evt.efam_note;
marriage_src = evt.efam_src;
witnesses;
}
in
let () = found_marriage := true in
loop l fam
| None -> (
match evt.efam_name with
| Efam_Divorce ->
if !found_divorce then loop l fam
else
let fam = { fam with divorce = Divorced evt.efam_date } in
let () = found_divorce := true in
loop l fam
| Efam_Separated ->
if !found_divorce then loop l fam
else
let fam = { fam with divorce = Separated } in
let () = found_divorce := true in
loop l fam
| _ -> loop l 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 =
let name =
match fam.relation with
| Married -> Efam_Marriage
| NotMarried -> Efam_NoMarriage
| Engaged -> Efam_Engage
| NoSexesCheckNotMarried -> Efam_NoMarriage
| NoMention -> Efam_NoMention
| NoSexesCheckMarried -> Efam_Marriage
| MarriageBann -> Efam_MarriageBann
| MarriageContract -> Efam_MarriageContract
| MarriageLicense -> Efam_MarriageLicense
| Pacs -> Efam_PACS
| Residence -> Efam_Residence
in
let witnesses = Array.map (fun ip -> (ip, Witness)) fam.witnesses in
let evt =
{
efam_name = name;
efam_date = fam.marriage;
efam_place = fam.marriage_place;
efam_reason = empty_string;
efam_note = fam.marriage_note;
efam_src = fam.marriage_src;
efam_witnesses = witnesses;
}
in
Some evt
in
let evt_div =
match fam.divorce with
| NotDivorced -> None
| Divorced cd ->
let evt =
{
efam_name = Efam_Divorce;
efam_date = cd;
efam_place = unique_string gen "";
efam_reason = unique_string gen "";
efam_note = unique_string gen "";
efam_src = unique_string gen "";
efam_witnesses = [||];
}
in
Some evt
| Separated ->
let evt =
{
efam_name = Efam_Separated;
efam_date = Date.cdate_None;
efam_place = unique_string gen "";
efam_reason = unique_string gen "";
efam_note = unique_string gen "";
efam_src = unique_string gen "";
efam_witnesses = [||];
}
in
Some evt
in
let fevents = [ evt_marr; evt_div ] in
let fevents =
List.fold_right
(fun evt fevents ->
match evt with Some evt -> evt :: fevents | None -> fevents)
fevents []
in
{ fam with 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 children 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 children'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
(* Look for inverted WIFE/HUSB *)
| (fath, ifath), (moth, imoth) when fath.m_sex = Female && moth.m_sex = Male
->
(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;
(* add father to witness' related persons *)
p.m_related <- ifath :: p.m_related;
ip)
witl
in
(* Events are sorted by chronological order (if equal then by alphabetical order) *)
let fevents =
Event.sort_events
(fun (name, _, _, _, _, _, _) -> Event.Fevent 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) ->
let p, ip = insert_somebody gen wit in
notice_sex gen p sex;
p.m_related <- ifath :: p.m_related;
(ip, wk))
witl
in
{
efam_name = fevent_name_unique_string gen name;
efam_date = date;
efam_place = unique_string gen place;
efam_reason = unique_string gen reason;
efam_note = unique_string gen notes;
efam_src = unique_string gen src;
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;
marriage_note = unique_string gen fo.marriage_note;
marriage_src = unique_string gen fo.marriage_src;
witnesses = Array.of_list witl;
relation = fo.relation;
divorce = fo.divorce;
fevents;
comment;
origin_file = unique_string gen fo.origin_file;
fsources;
fam_index = i;
}
(* create couple *)
and cpl = Adef.couple ifath imoth
(* created descandants *)
and des = { 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
gen.g_base.c_unions.(ifath) <- fath_uni;
let moth_uni = { family = Array.append moth_uni.family [| i |] } in
gen.g_base.c_unions.(imoth) <- moth_uni;
notice_sex gen fath fath_sex;
notice_sex gen moth moth_sex;
(* Append familly to the children's ascendant *)
Array.iter
(fun ix ->
let a = gen.g_base.c_ascends.(ix) in
(* check if children 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 | Epers_Cremation
| Epers_Accomplishment | Epers_Acquisition | Epers_Adhesion
| Epers_BaptismLDS | Epers_BarMitzvah | Epers_BatMitzvah | Epers_Benediction
| Epers_ChangeName | Epers_Circumcision | Epers_Confirmation
| Epers_ConfirmationLDS | Epers_Decoration | Epers_DemobilisationMilitaire
| Epers_Diploma | Epers_Distinction | Epers_Dotation | Epers_DotationLDS
| Epers_Education | Epers_Election | Epers_Emigration
| Epers_Excommunication | Epers_FamilyLinkLDS | Epers_FirstCommunion
| Epers_Funeral | Epers_Graduate | Epers_Hospitalisation | Epers_Illness
| Epers_Immigration | Epers_ListePassenger | Epers_MilitaryDistinction
| Epers_MilitaryPromotion | Epers_MilitaryService
| Epers_MobilisationMilitaire | Epers_Naturalisation | Epers_Occupation
| Epers_Ordination | Epers_Property | Epers_Recensement | Epers_Residence
| Epers_Retired | Epers_ScellentChildLDS | Epers_ScellentParentLDS
| Epers_ScellentSpouseLDS | Epers_VenteBien | Epers_Will ) as evt ->
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 (
Printf.printf "\nFile \"%s\"\n" fname;
Printf.printf "Individual events already defined for \"%s%s %s\"\n"
(sou gen.g_base p.m_first_name)
(if p.m_occ = 0 then "" else "." ^ string_of_int p.m_occ)
(sou gen.g_base p.m_surname);
check_error gen)
else
(* sort evenets *)
let pevents =
Event.sort_events
(fun (name, _, _, _, _, _, _) -> Event.Pevent 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
in
{
epers_name = pevent_name_unique_string gen name;
epers_date = date;
epers_place = unique_string gen place;
epers_reason = unique_string gen reason;
epers_note = unique_string gen notes;
epers_src = unique_string gen src;
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
try Some (find_person_by_name gen key.pk_first_name key.pk_surname occ)
with Not_found -> None
with
| Some ip ->
let p = poi gen.g_base ip in
if sou gen.g_base p.m_notes <> "" then (
Printf.printf "\nFile \"%s\"\n" fname;
Printf.printf "Notes already defined for \"%s%s %s\"\n"
key.pk_first_name
(if occ = 0 then "" else "." ^ string_of_int occ)
key.pk_surname;
check_error gen)
else p.m_notes <- unique_string gen str
| None ->
Printf.printf "File \"%s\"\n" fname;
Printf.printf "*** warning: undefined person: \"%s%s %s\"\n"
key.pk_first_name
(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
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
match NotesLinks.check_file_name nfname with
| Some (dl, f) -> List.fold_right Filename.concat dl f
| None -> "bad"
in
let bnotes =
let str =
match gen.g_file_info.f_bnotes with
| `drop -> assert false
| `erase -> str
| `merge -> old_nread nfname RnAll ^ str
| `first -> (
match old_nread nfname RnAll with "" -> str | str -> str)
in
{
nread = (fun f n -> if f = nfname then str else old_nread f n);
norigin_file = fname;
efiles =
(if nfname <> "" then
let efiles = gen.g_base.c_bnotes.efiles () in
fun () -> nfname :: efiles
else gen.g_base.c_bnotes.efiles);
}
in
gen.g_base.c_bnotes <- bnotes
(** Add wizard and his note to the [gen] *)
let insert_wiznote gen wizid str =
gen.g_wiznotes <- (wizid, str) :: gen.g_wiznotes
let map_option f = function 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 (
Printf.printf "\nFile \"%s\"\n" fname;
Printf.printf "Relations already defined for \"%s%s %s\"\n"
(sou gen.g_base p.m_first_name)
(if p.m_occ = 0 then "" else "." ^ string_of_int p.m_occ)
(sou gen.g_base p.m_surname);
check_error gen)
else (
notice_sex gen p sex;
let rl = List.map (insert_relation gen ip) rl in
p.m_rparents <- rl)
(** 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
| Notes (key, str) -> insert_notes fname gen key str
| Relations (sb, sex, rl) -> insert_relations fname gen sb sex rl
| Pevent (sb, _, pevents) -> insert_pevents fname gen sb pevents
| 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
let found_death = ref false in
let found_burial = ref false in
let death_std_fields = p.death in
let death_reason_std_fields =
match death_std_fields with Death (dr, _) -> dr | _ -> Unspecified
in
let rec loop pevents p =
match pevents with
| [] -> p
| evt :: l -> (
match evt.epers_name with
| Epers_Birth ->
if !found_birth then loop l p
else
let p =
{
p with
birth = evt.epers_date;
birth_place = evt.epers_place;
birth_note = evt.epers_note;
birth_src = evt.epers_src;
}
in
let () = found_birth := true in
loop l p
| Epers_Baptism ->
if !found_baptism then loop l p
else
let p =
{
p with
baptism = evt.epers_date;
baptism_place = evt.epers_place;
baptism_note = evt.epers_note;
baptism_src = evt.epers_src;
}
in
let () = found_baptism := true in
loop l p
| Epers_Death ->
if !found_death then loop l p
else
let death =
match Date.od_of_cdate evt.epers_date with
| Some d -> Death (death_reason_std_fields, Date.cdate_of_date d)
| None -> (
match death_std_fields with
| OfCourseDead -> OfCourseDead
| DeadYoung -> DeadYoung
| _ -> DeadDontKnowWhen)
in
let p =
{
p with
death;
death_place = evt.epers_place;
death_note = evt.epers_note;
death_src = evt.epers_src;
}
in
let () = found_death := true in
loop l p
| Epers_Burial ->
if !found_burial then loop l p
else
let p =
{
p with
burial = Buried evt.epers_date;
burial_place = evt.epers_place;
burial_note = evt.epers_note;
burial_src = evt.epers_src;
}
in
let () = found_burial := true in
loop l p
| Epers_Cremation ->
if !found_burial then loop l p
else
let p =
{
p with
burial = Cremated evt.epers_date;
burial_place = evt.epers_place;
burial_note = evt.epers_note;
burial_src = evt.epers_src;
}
in
let () = found_burial := true in
loop l p
| _ -> loop l 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 =
match Date.od_of_cdate p.birth with
| Some _ ->
let evt =
{
epers_name = Epers_Birth;
epers_date = p.birth;
epers_place = p.birth_place;
epers_reason = empty_string;
epers_note = p.birth_note;
epers_src = p.birth_src;
epers_witnesses = [||];
}
in
Some evt
| None ->
if p.birth_place = 0 && p.birth_src = 0 then None
else
let evt =
{
epers_name = Epers_Birth;
epers_date = p.birth;
epers_place = p.birth_place;
epers_reason = empty_string;
epers_note = p.birth_note;
epers_src = p.birth_src;
epers_witnesses = [||];
}
in
Some evt
in
let evt_bapt =
match Date.od_of_cdate p.baptism with
| Some _ ->
let evt =
{
epers_name = Epers_Baptism;
epers_date = p.baptism;
epers_place = p.baptism_place;
epers_reason = empty_string;
epers_note = p.baptism_note;
epers_src = p.baptism_src;
epers_witnesses = [||];
}
in
Some evt
| None ->
if p.baptism_place = 0 && p.baptism_src = 0 then None
else
let evt =
{
epers_name = Epers_Baptism;
epers_date = p.baptism;
epers_place = p.baptism_place;
epers_reason = empty_string;
epers_note = p.baptism_note;
epers_src = p.baptism_src;
epers_witnesses = [||];
}
in
Some evt
in
let evt_death =
match p.death with
| NotDead | DontKnowIfDead ->
if p.death_place = 0 && p.death_src = 0 then None
else
let evt =
{
epers_name = Epers_Death;
epers_date = Date.cdate_None;
epers_place = p.death_place;
epers_reason = empty_string;
epers_note = p.death_note;
epers_src = p.death_src;
epers_witnesses = [||];
}
in
Some evt
| Death (_, cd) ->
let date = Date.cdate_of_od (Some (Date.date_of_cdate cd)) in
let evt =
{
epers_name = Epers_Death;
epers_date = date;
epers_place = p.death_place;
epers_reason = empty_string;
epers_note = p.death_note;
epers_src = p.death_src;
epers_witnesses = [||];
}
in
Some evt
| DeadYoung | DeadDontKnowWhen | OfCourseDead ->
let evt =
{
epers_name = Epers_Death;
epers_date = Date.cdate_None;
epers_place = p.death_place;
epers_reason = empty_string;
epers_note = p.death_note;
epers_src = p.death_src;
epers_witnesses = [||];
}
in
Some evt
in
let evt_burial =
match p.burial with
| UnknownBurial ->
if p.burial_place = 0 && p.burial_src = 0 then None
else
let evt =
{
epers_name = Epers_Burial;
epers_date = Date.cdate_None;
epers_place = p.burial_place;
epers_reason = empty_string;
epers_note = p.burial_note;
epers_src = p.burial_src;
epers_witnesses = [||];
}
in
Some evt
| Buried cd ->
let evt =
{
epers_name = Epers_Burial;
epers_date = cd;
epers_place = p.burial_place;
epers_reason = empty_string;
epers_note = p.burial_note;
epers_src = p.burial_src;
epers_witnesses = [||];
}
in
Some evt
| Cremated cd ->
let evt =
{
epers_name = Epers_Cremation;
epers_date = cd;
epers_place = p.burial_place;
epers_reason = empty_string;
epers_note = p.burial_note;
epers_src = p.burial_src;
epers_witnesses = [||];
}
in
Some evt
in
let pevents = [ evt_birth; evt_bapt; evt_death; evt_burial ] in
let pevents =
List.fold_right
(fun evt pevents ->
match evt with Some evt -> evt :: pevents | None -> pevents)
pevents []
in
{ p with 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
(fun i mp ->
let p =
let c =
try
seek_in per_index_ic (sizeof_long * i);
let pos = input_binary_int per_index_ic in
seek_in per_ic pos;
input_char per_ic
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;
image = empty_string;
first_names_aliases = [];
surnames_aliases = [];
public_name = empty_string;
qualifiers = [];
aliases = [];
titles = [];
rparents = [];
related = [];
occupation = empty_string;
sex = Neuter;
access = IfTitles;
birth = Date.cdate_None;
birth_place = empty_string;
birth_note = empty_string;
birth_src = empty_string;
baptism = Date.cdate_None;
baptism_place = empty_string;
baptism_note = empty_string;
baptism_src = empty_string;
death = DontKnowIfDead;
death_place = empty_string;
death_note = empty_string;
death_src = empty_string;
burial = UnknownBurial;
burial_place = empty_string;
burial_note = empty_string;
burial_src = empty_string;
pevents = [];
notes = empty_string;
psources = empty_string;
key_index = 0;
}
| _ -> assert false
in
let p =
{
p with
first_name = mp.m_first_name;
surname = mp.m_surname;
occ = mp.m_occ;
rparents = mp.m_rparents;
related = mp.m_related;
pevents = mp.m_pevents;
sex = mp.m_sex;
notes = mp.m_notes;
key_index = i;
}
in
(* Si on a trouvé des évènements, on mets à jour *)
if p.pevents <> [] then update_person_with_pevents p
else update_pevents_with_person p)
persons
(** File containing the particles to use *)
let particules_file = ref ""
(** 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 = [||];
c_unions = [||];
c_families = [||];
c_couples = [||];
c_descends = [||];
c_strings = [||];
c_bnotes =
{ nread = (fun _ _ -> ""); 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 <- [||];
convert_persons per_index_ic per_ic a
in
let ascends =
let a = Array.sub gen.g_base.c_ascends 0 gen.g_pcnt in
gen.g_base.c_ascends <- [||];
a
in
let unions =
let a = Array.sub gen.g_base.c_unions 0 gen.g_pcnt in
gen.g_base.c_unions <- [||];
a
in
let _ =
Printf.eprintf "fcnt %d families %d\n" gen.g_fcnt
(Array.length gen.g_base.c_couples);
flush stderr
in
let families =
let a = Array.sub gen.g_base.c_families 0 gen.g_fcnt in
gen.g_base.c_families <- [||];
a
in
let couples =
let a = Array.sub gen.g_base.c_couples 0 gen.g_fcnt in
gen.g_base.c_couples <- [||];
a
in
let descends =
let a = Array.sub gen.g_base.c_descends 0 gen.g_fcnt in
gen.g_base.c_descends <- [||];
a
in
let _ =
Printf.eprintf "scnt %d strings %d\n" gen.g_scnt
(Array.length gen.g_base.c_strings);
flush stderr
in
let strings =
let a = Array.sub gen.g_base.c_strings 0 gen.g_scnt in
gen.g_base.c_strings <- [||];
a
in
Gwdb.make bname
(input_particles !particules_file)
( (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 (
(* FIXME ad hoc solution. wiznotes should be handled same as notes_d *)
if not @@ Sys.file_exists wizdir then Unix.mkdir wizdir 0o755;
List.iter
(fun (wizid, text) ->
let fname = Filename.concat wizdir wizid ^ ".txt" in
write_file_contents fname text)
wiznotes)
(** 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);
for i = 1 to Array.length Sys.argv - 1 do
Printf.fprintf oc " %s" Sys.argv.(i)
done;
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;
let tmp_per_index = Filename.concat tmp_dir "gwc_per_index" in
let tmp_per = Filename.concat tmp_dir "gwc_per" in
let tmp_fam_index = Filename.concat tmp_dir "gwc_fam_index" in
let tmp_fam = Filename.concat tmp_dir "gwc_fam" in
let fi =
{
f_local_names = Hashtbl.create 20011;
f_curr_src_file = "";
f_curr_gwo_file = "";
f_separate = false;
f_shift = 0;
f_bnotes = `merge;
}
in
let gen =
{
g_strings = Hashtbl.create 20011;
g_names = Hashtbl.create 20011;
g_pcnt = 0;
g_fcnt = 0;
g_scnt = 0;
g_file_info = fi;
g_base = empty_base;
g_patch_p = Hashtbl.create 20011;
g_wiznotes = [];
g_def = [||];
g_first_av_occ = Hashtbl.create 1;
g_errored = false;
g_per_index = open_out_bin tmp_per_index;
g_per = open_out_bin tmp_per;
g_fam_index = open_out_bin tmp_fam_index;
g_fam = open_out_bin tmp_fam;
}
in
let per_index_ic = open_in_bin tmp_per_index in
let per_ic = open_in_bin tmp_per in
let istr_empty = unique_string gen "" in
let istr_quest = unique_string gen "?" in
assert (istr_empty = 0);
assert (istr_quest = 1);
if Sys.unix then Sys.remove tmp_per_index;
if Sys.unix then Sys.remove tmp_per;
if Sys.unix then Sys.remove tmp_fam_index;
if Sys.unix then Sys.remove tmp_fam;
let next_family = next_family_fun fi in
(let rec loop () =
match next_family () with
| Some fam ->
insert_syntax fi.f_curr_src_file gen fam;
loop ()
| None -> ()
in
loop ());
close_out gen.g_per_index;
close_out gen.g_per;
close_out gen.g_fam_index;
close_out gen.g_fam;
Hashtbl.clear gen.g_strings;
Hashtbl.clear gen.g_names;
Hashtbl.clear fi.f_local_names;
Gc.compact ();
let base = make_base bdir gen per_index_ic per_ic in
Hashtbl.clear gen.g_patch_p;
if !do_check && gen.g_pcnt > 0 then (
Check.check_base base (set_error base gen) (set_warning base) ignore;
if !pr_stats then Stats.(print_stats base @@ stat_base base));
if not gen.g_errored then (
if !do_consang then ignore @@ ConsangAll.compute base true;
Gwdb.sync base;
output_wizard_notes bdir gen.g_wiznotes;
Mutil.remove_dir tmp_dir;
output_command_line bdir;
true)
else false