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

1330 lines
44 KiB
OCaml
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(* Copyright (c) 1998-2007 INRIA *)
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
type key = { pk_first_name : string; pk_surname : string; pk_occ : int }
(** Key to refer a person's definition *)
(** 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 *)
| Defined of (iper, iper, string) gen_person (** Person's definition *)
(** 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) *)
| Notes of key * string
(** Block that defines personal notes. First element represents
reference to person. Second is note's content. *)
| Relations of somebody * sex * (somebody, string) gen_relation list
(** 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. *)
| Pevent of
somebody
* sex
* (string gen_pers_event_name
* cdate
* string
* string
* string
* string
* (somebody * sex * witness_kind) list)
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)*)
| Bnotes of string * string
(** 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. *)
| Wnotes of string * string
(** Block that defines wizard notes. First string represents
First string represents wizard's id. Second is note's content. *)
(** {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 =
if i >= i2 then len
else if i = i2 - 1 then len + 1
else if s.[i] = '\\' then loop (len + 1) (i + 2)
else loop (len + 1) (i + 1)
in
loop 0 i1
in
let rec loop_copy t i j =
if i >= i2 then Bytes.unsafe_to_string t
else if i = i2 - 1 && s.[i] <> '_' then (
Bytes.set t j s.[i];
Bytes.unsafe_to_string t)
else
let c, i =
match s.[i] with
| '_' -> (' ', i)
| '\\' -> (s.[i + 1], i + 1)
| x -> (x, i)
in
Bytes.set t j c;
loop_copy t (succ i) (succ j)
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
match str.[i] with
| ' ' | '\t' ->
if beg = i then loop (succ beg) (succ i)
else copy_decode str beg i :: loop (succ i) (succ i)
| _ -> loop beg (succ i)
else if beg = i then []
else [ copy_decode str beg i ]
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
else if x = " " then ""
else
let start = if x.[0] = ' ' then 1 else 0 in
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 =
if i < String.length s && s.[i] = '-' then (true, i + 1) else (false, i)
in
let rec loop i n =
if i = String.length s then ((if neg then -n else n), i)
else
match s.[i] with
| '0' .. '9' as c ->
loop (succ i) ((10 * n) + Char.code c - Char.code '0')
| _ -> ((if neg then -n else n), i)
in
loop i 0
in
let skip_slash i =
if i < String.length s && s.[i] = '/' then Some (succ i) else None
in
let precision, i =
match s.[i] with
| '~' -> (About, succ i)
| '?' -> (Maybe, succ i)
| '>' -> (After, succ i)
| '<' -> (Before, succ i)
| _ -> (Sure, i)
in
let undefined, year, i =
let year, j = champ i in
if j = i + 1 && s.[i] = '0' then (true, year, j) else (false, year, j)
in
let error n = failwith (Printf.sprintf "date_of_string%d %s" n s) in
let dmy2 year2 i =
match skip_slash i with
| Some i -> (
let month2 = year2 in
let year2, i = champ i in
match skip_slash i with
| Some i ->
let day2 = month2 in
let month2 = year2 in
let year2, i = champ i in
if month2 < 1 || month2 > 13 then error 2
else if day2 < 1 || day2 > 31 then error 3
else ((day2, month2, year2), i)
| None ->
if month2 < 1 || month2 > 13 then error 4
else ((0, month2, year2), i))
| None -> ((0, 0, year2), i)
in
let date =
match skip_slash i with
| Some i -> (
let month = year in
let year, i = champ i in
match skip_slash i with
| Some i ->
let day = month in
let month = year in
let year, i = champ i in
(*
if year = 0 then if i = String.length s then None else error 1
else
*)
if month < 1 || month > 13 then error 2
else if day < 1 || day > 31 then error 3
else
let d = { day; month; year; prec = precision; delta = 0 } in
Some (Dgreg (d, Dgregorian), i)
| None ->
if year = 0 then None
else if month < 1 || month > 13 then error 4
else
let d = { day = 0; month; year; prec = precision; delta = 0 } in
Some (Dgreg (d, Dgregorian), i))
| None ->
if undefined then
if i = String.length s then None
else if s.[i] = '(' && s.[String.length s - 1] = ')' then
let txt = String.sub s (i + 1) (String.length s - i - 2) in
let txt = cut_space txt in
let txt = copy_decode txt 0 (String.length txt) in
Some (Dtext txt, String.length s)
else failwith ("date_of_string " ^ s)
else
let d = { day = 0; month = 0; year; prec = precision; delta = 0 } in
Some (Dgreg (d, Dgregorian), i)
in
let date =
match date with
| Some ((Dgreg (d, cal) as dt), i) ->
if i = String.length s then Some (dt, i)
else if s.[i] = '|' then
let year2, i = champ (succ i) in
let (day2, month2, year2), i = dmy2 year2 i in
let dmy2 = { day2; month2; year2; delta2 = 0 } in
Some (Dgreg ({ d with prec = OrYear dmy2 }, cal), i)
else if i + 1 < String.length s && s.[i] = '.' && s.[i + 1] = '.' then
let year2, i = champ (i + 2) in
let (day2, month2, year2), i = dmy2 year2 i in
let dmy2 = { day2; month2; year2; delta2 = 0 } in
Some (Dgreg ({ d with prec = YearInt dmy2 }, cal), i)
else Some (dt, i)
| Some ((Dtext _ as dt), i) -> Some (dt, i)
| None -> None
in
let date =
match date with
| Some (Dgreg (d, _), i) -> (
if i = String.length s then Some (Dgreg (d, Dgregorian), i)
else
match s.[i] with
| 'G' -> Some (Dgreg (d, Dgregorian), i + 1)
| 'J' ->
let d = Calendar.gregorian_of_julian d in
Some (Dgreg (d, Djulian), i + 1)
| 'F' ->
let d = Calendar.gregorian_of_french d in
Some (Dgreg (d, Dfrench), i + 1)
| 'H' ->
let d = Calendar.gregorian_of_hebrew d in
Some (Dgreg (d, Dhebrew), i + 1)
| _ -> Some (Dgreg (d, Dgregorian), i))
| d -> d
in
match date with
| 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;
if String.length line > 0 && line.[String.length line - 1] = '\r' then
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 birth date if it is present. *)
let get_optional_birthdate l =
match l with
| x :: l' -> (
let i = 0 in
if x.[i] = '!' then (None, l)
else
match x.[i] with
| '~' | '?' | '<' | '>' | '-' | '0' .. '9' ->
let d = date_of_string x i in
(Some d, l')
| _ -> (None, l))
| _ -> (None, l)
(** Parses person's baptism date if it is present. *)
let get_optional_baptdate l =
match l with
| x :: l' ->
let i = 0 in
if x.[i] = '!' then
let i = succ i in
match x.[i] with
| '~' | '?' | '<' | '>' | '-' | '0' .. '9' ->
let d = date_of_string x i in
(Some d, l')
| _ -> (None, l)
else (None, l)
| _ -> (None, l)
(** Parse death information if present. *)
let get_optional_deathdate l =
match l with
| "?" :: l' -> (Some DontKnowIfDead, l')
| "mj" :: l' -> (Some DeadYoung, l')
| "od" :: l' -> (Some OfCourseDead, l')
| x :: l' ->
let i = 0 in
let dr, i =
match x.[i] with
| 'k' -> (Killed, i + 1)
| 'm' -> (Murdered, i + 1)
| 'e' -> (Executed, i + 1)
| 's' -> (Disappeared, i + 1)
| _ -> (Unspecified, i)
in
if i < String.length x then
match x.[i] with
| '~' | '?' | '>' | '<' | '-' | '0' .. '9' ->
let d =
match date_of_string x i with
| None -> DeadDontKnowWhen
| Some d -> Death (dr, Date.cdate_of_date d)
in
(Some d, l')
| _ -> (None, l)
else (None, l)
| _ -> (None, l)
(** Parse burial information if present. *)
let get_burial l =
match l with
| "#buri" :: l -> (
match l with
| x :: l' ->
let i = 0 in
let od, l =
match x.[i] with
| '~' | '?' | '>' | '<' | '-' | '0' .. '9' ->
(date_of_string x i, l')
| _ -> (None, l)
in
(Buried (Date.cdate_of_od od), l)
| [] -> (Buried Date.cdate_None, l))
| "#crem" :: l -> (
match l with
| x :: l' ->
let i = 0 in
let od, l =
match x.[i] with
| '~' | '?' | '>' | '<' | '-' | '0' .. '9' ->
(date_of_string x i, l')
| _ -> (None, l)
in
(Cremated (Date.cdate_of_od od), l)
| [] -> (Cremated Date.cdate_None, l))
| _ -> (UnknownBurial, l)
(** Parse sex of person *)
let get_optional_sexe = function
| "h" :: l -> (Male, l)
| "f" :: l -> (Female, l)
| l -> (Neuter, l)
(** Parses int 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
else
match x.[i] with
| '0' .. '9' as c ->
loop true ((10 * n) + Char.code c - Char.code '0') (succ i)
| _ -> raise Not_found
in
loop false 0
(** Parses person's first name and occurence number.
Occurence number is 0 if not present. *)
let get_fst_name str l =
match l with
| x :: l' -> (
match x.[0] with
(*'a'..'z' | 'A'..'Z' | 'à'..'ÿ' | 'À'..'Ý' *)
| 'a' .. 'z'
| 'A' .. 'Z'
| '\xE0' .. '\xFF'
| '\xC0' .. '\xDD'
| '['
| '0' .. '9'
| '?' | ' ' ->
let x = cut_space x in
let x, occ =
match String.rindex_opt x '.' with
| Some i -> (
try (String.sub x 0 i, make_int x (succ i))
with Not_found -> (x, 0))
| None -> (x, 0)
in
(x, occ, l')
| _ -> 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' ->
if x.[0] = '{' && x.[String.length x - 1] = '}' then
let n = String.sub x 1 (String.length x - 2) in
let nl, l = get_fst_names_aliases str l' in
(cut_space n :: nl, 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)
| x :: l' -> (
match x.[0] with
| '{' -> ("", l)
(*'a'..'z' | 'A'..'Z' | 'à'..'ÿ' | 'À'..'Ý' *)
| 'a' .. 'z'
| 'A' .. 'Z'
| '\xE0' .. '\xFF'
| '\xC0' .. '\xDD'
| '0' .. '9'
| '?' | ' ' ->
(cut_space x, l')
| _ -> ("", l))
| _ -> ("", l)
(** Parses person's public name if present *)
let get_pub_name l =
match l with
| x :: l' ->
if x.[0] = '(' && x.[String.length x - 1] = ')' then
let a = String.sub x 1 (String.length x - 2) in
(cut_space a, 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 =
if i < String.length t then
match t.[i] with
| ':' -> (s, i + 1)
| '\\' -> loop (s ^ String.make 1 t.[i + 1]) (i + 2)
| c -> loop (s ^ String.make 1 c) (i + 1)
else (s, i)
in
loop "" i
in
let i = 0 in
let name, i =
let s, i = next_field i in
if i = String.length t then failwith t else (s, i)
in
let name = match name with "" -> Tnone | "*" -> Tmain | _ -> Tname name in
let title, i =
let s, i = next_field i in
if t.[i - 1] <> ':' then failwith t else (s, i)
in
let place, i = next_field i in
let date_start, i =
let d, i = next_field i in
((if d = "" then None else date_of_string d 0), i)
in
let date_end, i =
let d, i = next_field i in
((if d = "" then None else date_of_string d 0), i)
in
let nth, i =
let d, i = next_field i in
((if d = "" then 0 else int_of_string d), i)
in
if i <> String.length t then failwith t
else
{
t_name = name;
t_ident = title;
t_place = place;
t_date_start = Date.cdate_of_od date_start;
t_date_end = Date.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' ->
if x.[0] = '[' && x.[String.length x - 1] = ']' then
let t = String.sub x 1 (String.length x - 2) in
let t = scan_title t in
let al, l' = get_titles str l' in
((if t.t_ident = "" then al else t :: al), l')
else ([], l)
| _ -> ([], l)
(** Parses person's event name *)
let get_pevent_name str l =
match l with
| "#birt" :: l' -> (Epers_Birth, l')
| "#bapt" :: l' -> (Epers_Baptism, l')
| "#deat" :: l' -> (Epers_Death, l')
| "#buri" :: l' -> (Epers_Burial, l')
| "#crem" :: l' -> (Epers_Cremation, l')
| "#acco" :: l' -> (Epers_Accomplishment, l')
| "#acqu" :: l' -> (Epers_Acquisition, l')
| "#adhe" :: l' -> (Epers_Adhesion, l')
| "#awar" :: l' -> (Epers_Decoration, l')
| "#bapl" :: l' -> (Epers_BaptismLDS, l')
| "#barm" :: l' -> (Epers_BarMitzvah, l')
| "#basm" :: l' -> (Epers_BatMitzvah, l')
| "#bles" :: l' -> (Epers_Benediction, l')
| "#cens" :: l' -> (Epers_Recensement, l')
| "#chgn" :: l' -> (Epers_ChangeName, l')
| "#circ" :: l' -> (Epers_Circumcision, l')
| "#conf" :: l' -> (Epers_Confirmation, l')
| "#conl" :: l' -> (Epers_ConfirmationLDS, l')
| "#degr" :: l' -> (Epers_Diploma, l')
| "#demm" :: l' -> (Epers_DemobilisationMilitaire, l')
| "#dist" :: l' -> (Epers_Distinction, l')
| "#dotl" :: l' -> (Epers_DotationLDS, l')
| "#educ" :: l' -> (Epers_Education, l')
| "#elec" :: l' -> (Epers_Election, l')
| "#emig" :: l' -> (Epers_Emigration, l')
| "#endl" :: l' -> (Epers_Dotation, l')
| "#exco" :: l' -> (Epers_Excommunication, l')
| "#fcom" :: l' -> (Epers_FirstCommunion, l')
| "#flkl" :: l' -> (Epers_FamilyLinkLDS, l')
| "#fune" :: l' -> (Epers_Funeral, l')
| "#grad" :: l' -> (Epers_Graduate, l')
| "#hosp" :: l' -> (Epers_Hospitalisation, l')
| "#illn" :: l' -> (Epers_Illness, l')
| "#immi" :: l' -> (Epers_Immigration, l')
| "#lpas" :: l' -> (Epers_ListePassenger, l')
| "#mdis" :: l' -> (Epers_MilitaryDistinction, l')
| "#mobm" :: l' -> (Epers_MobilisationMilitaire, l')
| "#mpro" :: l' -> (Epers_MilitaryPromotion, l')
| "#mser" :: l' -> (Epers_MilitaryService, l')
| "#natu" :: l' -> (Epers_Naturalisation, l')
| "#occu" :: l' -> (Epers_Occupation, l')
| "#ordn" :: l' -> (Epers_Ordination, l')
| "#prop" :: l' -> (Epers_Property, l')
| "#resi" :: l' -> (Epers_Residence, l')
| "#reti" :: l' -> (Epers_Retired, l')
| "#slgc" :: l' -> (Epers_ScellentChildLDS, l')
| "#slgp" :: l' -> (Epers_ScellentParentLDS, l')
| "#slgs" :: l' -> (Epers_ScellentSpouseLDS, l')
| "#vteb" :: l' -> (Epers_VenteBien, l')
| "#will" :: l' -> (Epers_Will, l')
| s :: l' ->
if s.[0] = '#' then (Epers_Name (String.sub s 1 (String.length s - 1)), l')
else failwith str
| _ -> failwith str
(** Parses family event name *)
let get_fevent_name str l =
match l with
| "#marr" :: l' -> (Efam_Marriage, l')
| "#nmar" :: l' -> (Efam_NoMarriage, l')
| "#nmen" :: l' -> (Efam_NoMention, l')
| "#enga" :: l' -> (Efam_Engage, l')
| "#div" :: l' -> (Efam_Divorce, l')
| "#sep" :: l' -> (Efam_Separated, l')
| "#anul" :: l' -> (Efam_Annulation, l')
| "#marb" :: l' -> (Efam_MarriageBann, l')
| "#marc" :: l' -> (Efam_MarriageContract, l')
| "#marl" :: l' -> (Efam_MarriageLicense, l')
| "#pacs" :: l' -> (Efam_PACS, l')
| "#resi" :: l' -> (Efam_Residence, l')
| s :: l' ->
if s.[0] = '#' then (Efam_Name (String.sub s 1 (String.length s - 1)), l')
else failwith str
| _ -> failwith str
(** Parses event date if it is present. *)
let get_optional_event_date l =
match l with
| x :: l' -> (
let i = 0 in
if x.[i] = '!' then (None, l)
else
match x.[i] with
| '~' | '?' | '<' | '>' | '-' | '0' .. '9' ->
let d = date_of_string x i in
(Some d, l')
| _ -> (None, l))
| _ -> (None, l)
(** Parse witness kind *)
let get_event_witness_kind l =
match l with
| "#godp" :: l' -> (Witness_GodParent, l')
| "#offi" :: l' -> (Witness_CivilOfficer, l')
| "#reli" :: l' -> (Witness_ReligiousOfficer, l')
| "#info" :: l' -> (Witness_Informant, l')
| "#atte" :: l' -> (Witness_Attending, l')
| "#ment" :: l' -> (Witness_Mentioned, l')
| "#othe" :: l' -> (Witness_Other, 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 ->
let mar, l =
match x.[0] with
| '+' ->
( (if String.length x > 1 then Date.cdate_of_od (date_of_string x 1)
else Date.cdate_None),
l )
| _ -> failwith str
in
let relation, l =
let decode_sex v c l =
let decode_sex i =
match c.[i] with
| 'm' -> Male
| 'f' -> Female
| '?' -> Neuter
| _ -> failwith __LOC__
in
try ((v, decode_sex 0, decode_sex 1), l)
with _ -> ((v, Male, Female), c :: l)
in
match l with
| "#nm" :: l' -> ((NotMarried, Male, Female), l')
| "#eng" :: l' -> ((Engaged, Male, Female), l')
| "#noment" :: c :: l' when String.length c = 2 ->
decode_sex NoMention c l'
| "#noment" :: l' -> ((NoMention, Male, Female), l')
| "#nsck" :: c :: l' when String.length c = 2 ->
decode_sex NoSexesCheckNotMarried c l'
| "#nsckm" :: c :: l' when String.length c = 2 ->
decode_sex NoSexesCheckMarried c l'
| "#banns" :: c :: l' when String.length c = 2 ->
decode_sex MarriageBann c l'
| "#contract" :: c :: l' when String.length c = 2 ->
decode_sex MarriageContract c l'
| "#license" :: c :: l' when String.length c = 2 ->
decode_sex MarriageLicense c l'
| "#pacs" :: c :: l' when String.length c = 2 -> decode_sex Pacs c l'
| "#residence" :: c :: l' when String.length c = 2 ->
decode_sex Residence c l'
| _ -> ((Married, Male, Female), l)
in
let place, l = get_field "#mp" l in
let note, l = get_field "#mn" l in
let src, l = get_field "#ms" l in
let divorce, l =
match l with
| x :: l' when x.[0] = '-' ->
if String.length x > 1 then
(Divorced (Date.cdate_of_od (date_of_string x 1)), l')
else (Divorced Date.cdate_None, l')
| "#sep" :: l' -> (Separated, l')
| _ -> (NotDivorced, l)
in
(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 () =
{ (Mutil.empty_person "" "") with 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 following 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
let public_name, l = get_pub_name l in
let image, l = get_image l in
let qualifiers, l = get_qualifiers str l in
let aliases, l = get_aliases str l in
let titles, l = get_titles str l in
let access, l = get_access l in
let occupation, l = get_occu l in
let psources, l = get_sources l in
let naissance, l = get_optional_birthdate l in
let birth_place, l = get_field "#bp" l in
let birth_note, l = get_field "#bn" l in
let birth_src, l = get_field "#bs" l in
let baptism, l = get_optional_baptdate l in
let baptism_place, l =
let pp, l = get_field "#pp" l in
(* if no baptism 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
let bapt_src, l = get_field "#ps" l in
let mort, l = get_optional_deathdate l in
let death_place, l = get_field "#dp" l in
let death_note, l = get_field "#dn" l in
let death_src, l = get_field "#ds" l in
let mort =
match (naissance, mort) with
| None, _ | _, Some _ | Some None, _ -> (
match mort with Some m -> m | None -> DontKnowIfDead)
| Some _, None -> NotDead
in
let naissance =
match naissance with
| None -> Date.cdate_None
| Some x -> Date.cdate_of_od x
in
let baptism =
match baptism with None -> Date.cdate_None | Some x -> Date.cdate_of_od x
in
let burial, l = get_burial l in
let burial_place, l = get_field "#rp" l in
let burial_note, l = get_field "#rn" l in
let burial_src, l = get_field "#rs" l in
let u =
{
first_name = fn;
surname = sn;
occ;
rparents = u.rparents;
related = u.related;
sex;
notes = u.notes;
key_index = u.key_index;
first_names_aliases;
surnames_aliases;
public_name;
image;
qualifiers;
aliases;
titles;
access;
occupation;
psources = (if psources <> "" then psources else comm_psources);
birth = naissance;
birth_place =
(if birth_place <> "" then birth_place else comm_birth_place);
birth_note;
birth_src;
baptism;
baptism_place;
baptism_note = bapt_note;
baptism_src = bapt_src;
death = mort;
death_place;
death_note;
death_src;
burial;
burial_place;
burial_note;
burial_src;
pevents = u.pevents;
}
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
match l with [] -> false | s :: _ when s.[0] = '+' -> false | _ -> true
in
if not defined then
let key = { pk_first_name = pp; pk_surname = np; pk_occ = op } in
(Undefined key, np, l)
else
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 children and returns a person [gen_person] containing
all extracted information. If a children definition doesn't provide
surname then father's surname is used. ALso if it doesn't provide a children'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
let nom, l =
match l with
| "?" :: _ -> get_name l
| x :: _ -> (
match x.[0] with
| '<' | '>' | '!' | '~' | '?' | '-' | '0' .. '9' | '{' | '#' ->
(surname, l)
| '(' | '[' -> ((if prenom = "" then "" else surname), l)
| _ -> get_name l)
| _ -> (surname, [])
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 -> (
let rtyp =
match x with
| "adop" | "adop:" -> Adoption
| "reco" | "reco:" -> Recognition
| "cand" | "cand:" -> CandidateParent
| "godp" | "godp:" -> GodParent
| "fost" | "fost:" -> FosterParent
| _ -> failwith str
in
if String.length x = 5 && x.[4] = ':' then (
let fk, _, l = parse_parent str l in
let l = match l with "+" :: l -> l | _ -> failwith str in
let mk, _, l = parse_parent str l in
if l <> [] then failwith str;
{ r_type = rtyp; r_fath = Some fk; r_moth = Some mk; r_sources = "" })
else
match l with
| "fath:" :: l ->
let fk, _, l = parse_parent str l in
if l <> [] then failwith str;
{ r_type = rtyp; r_fath = Some fk; r_moth = None; r_sources = "" }
| "moth:" :: l ->
let mk, _, l = parse_parent str l in
if l <> [] then failwith str;
{ r_type = rtyp; r_fath = None; r_moth = Some mk; r_sources = "" }
| _ -> failwith str)
| _ -> failwith str
(** Read notes of a person inside [note] block across multiple lines and
concat them. *)
let read_notes ic =
let notes =
try
let rec loop = function
| "end notes" -> ""
| l -> l ^ "\n" ^ loop (input_a_line ic)
in
loop (input_a_line ic)
with End_of_file -> failwith "end of file"
in
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
let rec loop s =
if s = end_txt then ""
else
let len = String.length s in
let s =
if len > 2 && s.[0] = ' ' && s.[1] = ' ' then
String.sub s 2 (len - 2)
else s
in
s ^ "\n" ^ loop (input_a_line ic)
in
loop (input_a_line ic)
with End_of_file -> failwith "end of file"
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_enc_utf_8 (** Read block that defines that file use utf-8 encoding *)
| F_gw_plus (** Read block that defines that the file uses gwplus syntax *)
| F_none (** Read end of the file *)
| F_fail of string (** Exception while reading *)
(** Read succesive family note lines and concat it. *)
let loop_note line ic =
let rec loop_note acc str =
match fields str with
| "note" :: tl ->
let note =
if tl = [] then ""
else
String.sub str
(String.length "note" + 1)
(String.length str - String.length "note" - 1)
in
loop_note (note :: acc) (input_a_line ic)
| _ -> (String.concat "\n" (List.rev @@ ("" :: acc)), str)
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
| ("wit" | "wit:") :: l ->
let sex, l =
match l with
| "m:" :: l -> (Male, l)
| "f:" :: l -> (Female, l)
| l -> (Neuter, l)
in
let wkind, l = get_event_witness_kind l in
let wk, _, l = parse_parent str l in
if l <> [] then failwith str;
loop_witn ((wk, sex, wkind) :: acc) (input_a_line ic)
| _ -> (List.rev acc, str)
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
(* 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
| Some (str, ("wit" | "wit:") :: l) ->
let sex, l =
match l with
| "m:" :: l -> (Male, l)
| "f:" :: l -> (Female, l)
| l -> (Neuter, l)
in
let wk, _, l = parse_parent str l in
if l <> [] then failwith str;
let witn, line = loop (read_line ic) in
((wk, sex) :: witn, line)
| line -> ([], line)
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" :: _) ->
let comm = String.sub str 5 (String.length str - 5) in
(comm, read_line ic)
| _ -> ("", line)
in
(* read family events *)
let fevents, line =
match line with
| Some (_, "fevt" :: _) ->
let fevents, line =
let rec loop fevents = function
| "end fevt" -> (fevents, read_line ic)
| x ->
let str, l = (x, fields x) in
(* On récupère le nom, date, lieu, source, cause *)
let name, l = get_fevent_name str l in
let date, l = get_optional_event_date l in
let place, l = get_field "#p" l in
let cause, l = get_field "#c" l in
let src, l = get_field "#s" l in
let date =
match date with
| None -> Date.cdate_None
| Some x -> Date.cdate_of_od x
in
if l <> [] then failwith str;
(* On récupère les témoins *)
let witn, line = loop_witn (input_a_line ic) ic in
(* On récupère les notes *)
let notes, line = loop_note line ic in
let notes = Mutil.strip_all_trailing_spaces notes in
let evt = (name, date, place, cause, src, notes, witn) in
loop (evt :: fevents) line
in
loop [] (input_a_line ic)
in
(List.rev fevents, line)
| _ -> ([], line)
in
match line with
(* have children *)
| Some (_, [ "beg" ]) ->
let cles_enfants =
let rec loop children =
match read_line ic with
| Some (str, "-" :: l) ->
let sex, l = get_optional_sexe l in
let child, l = parse_child str surname sex csrc cbp l in
if l <> [] then failwith str else loop (child :: children)
| Some (_, [ "end" ]) -> children
| Some (str, _) -> failwith str
| _ -> failwith "eof"
in
List.rev (loop [])
in
(* create a family definition (without witnesses, events and family index) *)
let fo =
{
marriage;
marriage_place = marr_place;
marriage_note = marr_note;
marriage_src = marr_src;
witnesses = [||];
relation;
divorce;
fevents = [];
comment = comm;
origin_file = Filename.basename fname;
fsources = fsrc;
fam_index = Gwdb.dummy_ifam;
}
in
let deo = { children = Array.of_list cles_enfants } in
F_some
( Family (co, fath_sex, moth_sex, witn, fevents, fo, deo),
read_line ic )
(* no children *)
| line ->
let fo =
{
marriage;
marriage_place = marr_place;
marriage_note = marr_note;
marriage_src = marr_src;
witnesses = [||];
relation;
divorce;
fevents = [];
comment = comm;
origin_file = Filename.basename fname;
fsources = fsrc;
fam_index = Gwdb.dummy_ifam;
}
in
let deo = { children = [||] } in
F_some (Family (co, fath_sex, moth_sex, witn, fevents, fo, deo), line)
)
(* 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
String.sub str len (String.length str - len)
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" ]) ->
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
if l <> [] then failwith "str"
else
match read_line ic with
| Some (_, [ "beg" ]) ->
let notes = read_notes ic in
let key =
{ pk_first_name = first_name; pk_surname = surname; pk_occ = occ }
in
F_some (Notes (key, notes), read_line ic)
| Some (str, _) -> failwith str
| None -> failwith "end of file")
(* Wizard note block *)
| Some (str, "wizard-note" :: _) ->
let wizid =
let len = String.length "wizard-note " in
String.sub str len (String.length str - len)
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)
| "#f" :: l -> (Female, l)
| l -> (Neuter, l)
in
if l <> [] then failwith "str"
else
match read_line ic with
(* Read list of relations *)
| Some (_, [ "beg" ]) ->
let rl =
try
let rec loop = function
| "end" -> []
| x -> get_relation x (fields x) :: loop (input_a_line ic)
in
loop (input_a_line ic)
with End_of_file -> failwith "missing end rel"
in
F_some (Relations (sb, sex, rl), read_line ic)
| Some (str, _) -> failwith str
| None -> failwith "end of file")
(* 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
let pevents =
let rec loop pevents = function
| "end pevt" -> pevents
| x ->
let str, l = (x, fields x) in
(* On récupère le nom, date, lieu, source, cause *)
let name, l = get_pevent_name str l in
let date, l = get_optional_event_date l in
let place, l = get_field "#p" l in
let cause, l = get_field "#c" l in
let src, l = get_field "#s" l in
let date =
match date with
| None -> Date.cdate_None
| Some x -> Date.cdate_of_od x
in
if l <> [] then failwith str;
(* On récupère les témoins *)
let witn, line = loop_witn (input_a_line ic) ic in
(* On récupère les notes *)
let notes, line = loop_note line ic in
let notes = Mutil.strip_all_trailing_spaces notes in
let evt = (name, date, place, cause, src, notes, witn) in
loop (evt :: pevents) line
in
loop [] (input_a_line ic)
in
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
(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
| F_some (family, line) ->
output_value oc (family : gw_syntax);
loop line encoding
| F_enc_utf_8 -> loop (read_line (ic, E_utf_8)) E_utf_8
| F_gw_plus ->
create_all_keys := true;
loop (read_line (ic, encoding)) encoding
| F_none -> ()
| F_fail str ->
Printf.printf "File \"%s\", line %d:\n" x !line_cnt;
Printf.printf "Error: %s\n" str;
flush stdout;
loop (read_line (ic, encoding)) encoding
in
loop (read_line (ic, E_iso_8859_1)) E_iso_8859_1;
close_in ic
with e ->
close_out oc;
Mutil.rm out_file;
raise e);
close_out oc