Commit Diff


commit - 6f8321e65ea37dceb515d4d8e7af11429aed9cc8
commit + 6fa62bae5d2b8a1d41f16837a255b243f8f85ec3
blob - 0c47c893e2c60ff4baa66a37594da624fa88c0ff (mode 644)
blob + /dev/null
--- cmd/gedcom2errors/main.go
+++ /dev/null
@@ -1,705 +0,0 @@
-package main
-
-import (
-	"bytes"
-	"errors"
-	"flag"
-	"fmt"
-	"github.com/iand/gedcom"
-	"io/ioutil"
-	"os"
-	"regexp"
-	"strings"
-	"time"
-)
-
-const (
-	OLDAGE = 99
-	WEDDER = 3
-	MAYDEC = 5
-	YNGMAR = 20
-	OLDMAR = 80
-	LNGWDW = 80
-	OLDUNM = 67
-	FECMOM = 45
-	OLDMOM = 55
-	YNGMOM = 16
-	CSPACE = 1
-	CBSPAN = 1
-)
-
-type fn_person func(person *gedcom.IndividualRecord) bool
-type fn_family func(family *gedcom.FamilyRecord) bool
-
-func rule_I100(person *gedcom.IndividualRecord) bool {
-	var birt_date, deat_date time.Time
-	birt_date = eventDate(person, "BIRT")
-	if birt_date.IsZero() {
-		return true
-	}
-	deat_date = eventDate(person, "DEAT")
-	if deat_date.Sub(birt_date) > OLDAGE {
-		fmt.Println(deat_date, birt_date)
-		return false
-	}
-
-	return true
-}
-
-func rule_I101(person *gedcom.IndividualRecord) bool {
-	var birt_date, bap_date time.Time
-	birt_date = eventDate(person, "BIRT")
-	if birt_date.IsZero() {
-		return true
-	}
-	bap_date = eventDate(person, "BAP")
-	if bap_date.Sub(birt_date) > 0 {
-		fmt.Println(birt_date, bap_date)
-		return false
-	}
-
-	return true
-}
-
-func rule_I102(person *gedcom.IndividualRecord) bool {
-	var birt_date, deat_date time.Time
-	birt_date = eventDate(person, "BIRT")
-	if birt_date.IsZero() {
-		return true
-	}
-	deat_date = eventDate(person, "DEAT")
-	if deat_date.Sub(birt_date) < 0 {
-		fmt.Println(birt_date, deat_date)
-		return false
-	}
-
-	return true
-}
-
-func rule_I103(person *gedcom.IndividualRecord) bool {
-	var deat_date, buri_date time.Time
-	deat_date = eventDate(person, "DEAT")
-	if deat_date.IsZero() {
-		return true
-	}
-	buri_date = eventDate(person, "BURI")
-	if deat_date.Sub(buri_date) < 0 {
-		fmt.Println(deat_date, buri_date)
-		return false
-	}
-
-	return true
-}
-
-func rule_I104(person *gedcom.IndividualRecord) bool {
-	var deat_date, bap_date time.Time
-	deat_date = eventDate(person, "DEAT")
-	if deat_date.IsZero() {
-		return true
-	}
-	bap_date = eventDate(person, "BAP")
-	if deat_date.Sub(bap_date) < 0 {
-		fmt.Println(deat_date, bap_date)
-		return false
-	}
-
-	return true
-}
-
-func rule_I105(person *gedcom.IndividualRecord) bool {
-	var bap_date, buri_date time.Time
-	bap_date = eventDate(person, "BAP")
-	if bap_date.IsZero() {
-		return true
-	}
-	buri_date = eventDate(person, "BURI")
-	if bap_date.Sub(buri_date) < 0 {
-		fmt.Println(buri_date, bap_date)
-		return false
-	}
-
-	return true
-}
-
-func rule_I106(person *gedcom.IndividualRecord) bool {
-	var buri_date, deat_date time.Time
-	deat_date = eventDate(person, "DEAT")
-	if deat_date.IsZero() {
-		return true
-	}
-	buri_date = eventDate(person, "BURI")
-	if deat_date.Sub(buri_date) > 0 {
-		fmt.Println(buri_date, deat_date)
-		return false
-	}
-
-	return true
-}
-
-func rule_I107(person *gedcom.IndividualRecord) bool {
-	var bap_date, birt_date time.Time
-	bap_date = eventDate(person, "BAP")
-	if bap_date.IsZero() {
-		return true
-	}
-	birt_date = eventDate(person, "BIRT")
-	if bap_date.Sub(birt_date) >= 1 {
-		fmt.Println(bap_date, birt_date)
-		return false
-	}
-
-	return true
-}
-
-func rule_I108(person *gedcom.IndividualRecord) bool {
-	var deat_date, buri_date time.Time
-	deat_date = eventDate(person, "DEAT")
-	if deat_date.IsZero() {
-		return true
-	}
-	buri_date = eventDate(person, "BURI")
-	if deat_date.Sub(buri_date) >= 1 {
-		fmt.Println(deat_date, buri_date)
-		return false
-	}
-
-	return true
-}
-
-func rule_I109(person *gedcom.IndividualRecord) bool {
-	if person.Sex == "" {
-		return false
-	}
-
-	return true
-}
-
-func rule_I110(person *gedcom.IndividualRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_I111(person *gedcom.IndividualRecord) bool {
-	if len(person.Parents) > 1 {
-		return false
-	}
-
-	return true
-}
-
-func rule_I112(person *gedcom.IndividualRecord) bool {
-	if len(person.Family) == 0 {
-		return false
-	}
-
-	return true
-}
-
-func rule_F100(family *gedcom.FamilyRecord) bool {
-	if family.Husband == nil {
-		return false
-	}
-	if family.Wife == nil {
-		return false
-	}
-	if len(family.Child) == 0 {
-		return false
-	}
-
-	return true
-}
-
-func rule_F101(family *gedcom.FamilyRecord) bool {
-	if family.Husband == nil {
-		return false
-	}
-	if family.Wife == nil {
-		return false
-	}
-
-	return true
-}
-
-func rule_F102(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_F103(family *gedcom.FamilyRecord) bool {
-	if family.Husband == nil {
-		return false
-	}
-
-	return true
-}
-
-func rule_F104(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_F105(family *gedcom.FamilyRecord) bool {
-	if family.Wife == nil {
-		return false
-	}
-
-	return true
-}
-
-func rule_F106(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_F107(family *gedcom.FamilyRecord) bool {
-	if len(family.Child) == 0 {
-		return false
-	}
-
-	return true
-}
-
-func rule_F108(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_F109(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_F110(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M100(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M101(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M102(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M103(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M104(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M105(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M106(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M107(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M108(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M109(family *gedcom.FamilyRecord) bool {
-	if family.Husband.Sex == family.Wife.Sex {
-		return false
-	}
-
-	return true
-}
-
-func rule_M110(family *gedcom.FamilyRecord) bool {
-	if family.Husband.Sex == "F" {
-		return false
-	}
-
-	return true
-}
-
-func rule_M111(family *gedcom.FamilyRecord) bool {
-	if family.Wife.Sex == "M" {
-		return false
-	}
-
-	return true
-}
-
-func rule_M112(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M113(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M114(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_M115(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_P100(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_P101(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_P102(family *gedcom.FamilyRecord) bool {
-	var mother_birt_date, child_birt_date time.Time
-	mother_birt_date = eventDate(family.Wife, "BIRT")
-	for _, child := range family.Child {
-		child_birt_date = eventDate(child, "BIRT")
-		if mother_birt_date.Sub(child_birt_date) < 0 {
-			return false
-		}
-	}
-
-	return true
-}
-
-func rule_P103(family *gedcom.FamilyRecord) bool {
-	var mother_birt_date, child_birt_date time.Time
-	mother_birt_date = eventDate(family.Wife, "BIRT")
-	if mother_birt_date.IsZero() {
-		return true
-	}
-	for _, child := range family.Child {
-		child_birt_date = eventDate(child, "BIRT")
-		if mother_birt_date.Sub(child_birt_date) < YNGMOM {
-			fmt.Println(mother_birt_date, child_birt_date)
-			return false
-		}
-	}
-
-	return true
-}
-
-func rule_P104(family *gedcom.FamilyRecord) bool {
-	var mother_deat_date, child_birt_date time.Time
-	mother_deat_date = eventDate(family.Wife, "DEAT")
-	if mother_deat_date.IsZero() {
-		return true
-	}
-	for _, child := range family.Child {
-		child_birt_date = eventDate(child, "BIRT")
-		if mother_deat_date == child_birt_date {
-			fmt.Println(mother_deat_date, child_birt_date)
-			return false
-		}
-	}
-
-	return true
-}
-
-func rule_P105(family *gedcom.FamilyRecord) bool {
-	var father_deat_date, child_birt_date time.Time
-	father_deat_date = eventDate(family.Husband, "DEAT")
-	if father_deat_date.IsZero() {
-		return true
-	}
-	for _, child := range family.Child {
-		child_birt_date = eventDate(child, "BIRT")
-		if father_deat_date == child_birt_date {
-			fmt.Println(father_deat_date, child_birt_date)
-			return false
-		}
-	}
-
-	return true
-}
-
-func rule_P106(family *gedcom.FamilyRecord) bool {
-	var father_surname []string
-	var child_surname []string
-	surname_re := regexp.MustCompile(`.+ /(.+)/`)
-	for _, name := range family.Husband.Name {
-		surname := surname_re.FindStringSubmatch(name.Name)
-		if surname[1] != "" {
-			father_surname = append(father_surname, surname[1])
-		}
-	}
-	for _, child := range family.Child {
-		for _, name := range child.Name {
-			surname := surname_re.FindStringSubmatch(name.Name)
-			if surname[1] != "" {
-				child_surname = append(child_surname, surname[1])
-			}
-		}
-	}
-
-	for _, surname := range child_surname {
-		if !contains(father_surname, surname) {
-			fmt.Println(father_surname, surname)
-			return false
-		}
-	}
-
-	return true
-}
-
-func rule_C100(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_C101(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_C102(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_C103(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_C104(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func rule_C105(family *gedcom.FamilyRecord) bool {
-	/* FIXME */
-	return true
-}
-
-func eventDate(person *gedcom.IndividualRecord, eventTag string) time.Time {
-	var date time.Time
-	for _, event := range person.Event {
-		if event.Tag == eventTag {
-			date, _ = parse_date_string(event.Date)
-		}
-	}
-
-	return date
-}
-
-func parse_date_string(date string) (time.Time, error) {
-	// "2006-01-02T15:04:05.000Z"
-	layout := "02 JAN 2006"
-	t, err := time.Parse(layout, date)
-	if err == nil {
-		return t, nil
-	}
-
-	return time.Time{}, err
-}
-
-func contains(s []string, e string) bool {
-	for _, a := range s {
-		if a == e {
-			return true
-		}
-	}
-	return false
-}
-
-func PrintIndividualRecord(record *gedcom.IndividualRecord) {
-	fmt.Printf("Person: %s", record.Xref)
-	if len(record.Name) > 0 {
-		fmt.Printf(" %s\n", record.Name[0].Name)
-	} else {
-		fmt.Printf("\n")
-	}
-}
-
-func PrintFamilyRecord(record *gedcom.FamilyRecord) {
-	fmt.Printf("Family %s\n", record.Xref)
-}
-
-func main() {
-	/*
-		persons born after they were married
-		persons born after their children
-		persons having similarly named children
-		persons having an ancestral loop
-
-		Persons Born After One of Their Parents Died
-		Persons Born After One of Their Parents Was Buried
-		Persons Baptized After Being Buried
-		Persons Baptized After Being Married
-		Persons Married After Being Buried
-		Persons Baptized After They Died
-		Persons Married After They Died
-		Persons Died Before Children Were Born
-		Persons Died After Being Buried
-		Persons Buried Before Having Children
-		Persons Baptized Past the Age of 5
-		Persons Married Before the Age of 14
-		Wives Married After the Age of 50
-		Persons Living Past the Age of 100
-		Persons at Least 30 Years Older Than Their Spouse
-		Persons Having a Degree of Kinship of 4 or Less
-		Persons Having a Non-biological Parent
-		Persons Whose Birth Dates Could Not Be Estimated
-		Persons with Invalid Dates
-		Families Having Swapped Spouses
-	*/
-
-	// rules are borrowed from gigatrees.com and lifelines
-	person_rules := map[string]fn_person{
-		"EI100: person's age at death is older than _oldage_": rule_I100,
-		"EI101: person is baptized before birth":              rule_I101,
-		"EI102: person dies before birth":                     rule_I102,
-		"EI103: person is buried before birth":                rule_I103,
-		"EI104: person dies before baptism":                   rule_I104,
-		"EI105: person is buried before baptism":              rule_I105,
-		"EI106: person is buried before death":                rule_I106,
-		"EI107: person is baptised after birth year":          rule_I107,
-		"EI108: person is buried after death year":            rule_I108,
-		"EI109: person has unkown gender":                     rule_I109,
-		"EI110: person has ambiguous gender":                  rule_I110,
-		"EI111: person has multiple parentage":                rule_I111,
-		"EI112: person has no family pointers":                rule_I112,
-	}
-
-	family_rules := map[string]fn_family{
-		"EF100: family has no members":             rule_F100,
-		"EF101: family has no parents":             rule_F101,
-		"EF102: husband missing pointer to family": rule_F102,
-		"EF103: family missing pointer to husband": rule_F103,
-		"EF104: wife missing pointer to family":    rule_F104,
-		"EF105: family missing pointer to wife":    rule_F105,
-		"EF106: child missing pointer to family":   rule_F106,
-		"EF107: family missing pointer to child":   rule_F107,
-		"EF108: family has multiple husbands":      rule_F108,
-		"EF109: family has multiple wives":         rule_F109,
-		"EF110: child is in family multiple times": rule_F110,
-		// marriage rules
-		"EM100: person marries before birth":                                            rule_M100,
-		"EM101: person marries after death":                                             rule_M101,
-		"EM102: person has more than WEDDER spouses":                                    rule_M102,
-		"EM103: person marries someone more than MAYDEC years older":                    rule_M103,
-		"EM104: person marries younger than YNGMAR":                                     rule_M104,
-		"EM105: person marries older than OLDMAR":                                       rule_M105,
-		"EM106: marriage out of order":                                                  rule_M106,
-		"EM107: marriage before birth from previous marriage":                           rule_M107,
-		"EM108: marriage after birth from subsequent marriage":                          rule_M108,
-		"EM109: homosexual marriage":                                                    rule_M109,
-		"EM110: person is a female husband":                                             rule_M110,
-		"EM111: person is a male wife":                                                  rule_M111,
-		"EM112: person was a widow(er) longer than LNGWDW years":                        rule_M112,
-		"EM113: person lived more than OLDUNM years and never married":                  rule_M113,
-		"EM114: person has multiple marriages, this one with no spouse and no children": rule_M114,
-		"EM115: person has same surname as spouse":                                      rule_M115,
-		// parentage rules
-		"EP100: mother has more than FECMOM children":                    rule_P100,
-		"EP101: mother is older than OLDMOM at time of birth of child":   rule_P101,
-		"EP102: child is born before mother":                             rule_P102,
-		"EP103: mother is younger than YNGMOM at time of birth of child": rule_P103,
-		"EP104: mother is dead at birth of child":                        rule_P104,
-		"EP105: father is dead at birth of child":                        rule_P105,
-		"EP106: child doesn't inherit father's surname":                  rule_P106,
-		// children rules
-		"EC100: child is born out of order with respect to a previous child": rule_C100,
-		"EC101: child is born in the same year as a previous child":          rule_C101,
-		"EC102: child is born more than CSPACE years after previous child":   rule_C102,
-		"EC103: children's births span more than CBSPAN years":               rule_C103,
-		"EC104: child is born before parent's marriage":                      rule_C104,
-		"EC105: child has same given name as sibling":                        rule_C105,
-	}
-
-	flag.Usage = func() {
-		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
-		fmt.Fprintf(os.Stderr, "\nTool for checking of GEDCOM file for errors.")
-		fmt.Fprintf(os.Stderr, "\n\nFlags:\n")
-		flag.PrintDefaults()
-	}
-
-	var gedfile = flag.String("file", "", "GEDCOM filename")
-	var ignorelist = flag.String("ignore", "", "rules ignore list")
-	var verbose = flag.Bool("verbose", false, "verbose mode")
-
-	flag.Parse()
-
-	if *gedfile == "" {
-		flag.Usage()
-		os.Exit(1)
-	}
-
-	var ignores []string
-	if len(*ignorelist) != 0 {
-		ignores = strings.Split(*ignorelist, ",")
-		fmt.Println("These rules are ignored:", ignores)
-	}
-
-	data, _ := ioutil.ReadFile(*gedfile)
-	d := gedcom.NewDecoder(bytes.NewReader(data))
-	g, _ := d.Decode()
-
-	if *verbose {
-		fmt.Printf("Found %d persons:\n\n", len(g.Individual))
-	}
-
-	found_errors := false
-	for _, record := range g.Individual {
-		for err_msg, fn := range person_rules {
-			if contains(ignores, err_msg) {
-				continue
-			}
-			if !fn(record) {
-				fmt.Printf("%s", record.Xref)
-				if len(record.Name) > 0 {
-					fmt.Printf(" %s\n", record.Name[0].Name)
-				} else {
-					fmt.Printf("\n")
-				}
-				fmt.Println(errors.New(err_msg))
-				found_errors = true
-			}
-		}
-	}
-
-	if *verbose {
-		fmt.Printf("\nFound %d families:\n\n", len(g.Family))
-	}
-	for _, record := range g.Family {
-		for err_msg, fn := range family_rules {
-			if contains(ignores, err_msg) {
-				continue
-			}
-			if !fn(record) {
-				PrintFamilyRecord(record)
-				fmt.Println(err_msg)
-				found_errors = true
-			}
-		}
-	}
-
-	if found_errors {
-		os.Exit(1)
-	}
-}
blob - /dev/null
blob + 6f8effcc98ba58c6640857d2db328b188b2f4ce2 (mode 644)
--- /dev/null
+++ cmd/gedcom2errors/gedcom2errors.go
@@ -0,0 +1,735 @@
+package main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"github.com/iand/gedcom"
+	"io/ioutil"
+	"os"
+	"regexp"
+	"strings"
+	"time"
+)
+
+const (
+	OLDAGE = 99
+	WEDDER = 3
+	MAYDEC = 5
+	YNGMAR = 20
+	OLDMAR = 80
+	LNGWDW = 80
+	OLDUNM = 67
+	FECMOM = 45
+	OLDMOM = 55
+	YNGMOM = 16
+	CSPACE = 1
+	CBSPAN = 1
+)
+
+type fn_person func(person *gedcom.IndividualRecord) bool
+type fn_family func(family *gedcom.FamilyRecord) bool
+
+func rule_I100(person *gedcom.IndividualRecord) bool {
+	var birt_date, deat_date time.Time
+	birt_date = eventDate(person, "BIRT")
+	if birt_date.IsZero() {
+		return true
+	}
+	deat_date = eventDate(person, "DEAT")
+	if deat_date.IsZero() {
+		return true
+	}
+	if deat_date.Sub(birt_date) > OLDAGE {
+		fmt.Println(deat_date, birt_date)
+		return false
+	}
+
+	return true
+}
+
+func rule_I101(person *gedcom.IndividualRecord) bool {
+	var birt_date, bap_date time.Time
+	birt_date = eventDate(person, "BIRT")
+	if birt_date.IsZero() {
+		return true
+	}
+	bap_date = eventDate(person, "BAP")
+	if bap_date.IsZero() {
+		return true
+	}
+	if bap_date.Sub(birt_date) > 0 {
+		fmt.Println(birt_date, bap_date)
+		return false
+	}
+
+	return true
+}
+
+func rule_I102(person *gedcom.IndividualRecord) bool {
+	var birt_date, deat_date time.Time
+	birt_date = eventDate(person, "BIRT")
+	if birt_date.IsZero() {
+		return true
+	}
+	deat_date = eventDate(person, "DEAT")
+	if deat_date.IsZero() {
+		return true
+	}
+	if deat_date.Sub(birt_date) < 0 {
+		fmt.Println(birt_date, deat_date)
+		return false
+	}
+
+	return true
+}
+
+func rule_I103(person *gedcom.IndividualRecord) bool {
+	var deat_date, buri_date time.Time
+	deat_date = eventDate(person, "DEAT")
+	if deat_date.IsZero() {
+		return true
+	}
+	buri_date = eventDate(person, "BURI")
+	if buri_date.IsZero() {
+		return true
+	}
+	if deat_date.Sub(buri_date) < 0 {
+		fmt.Println(deat_date, buri_date)
+		return false
+	}
+
+	return true
+}
+
+func rule_I104(person *gedcom.IndividualRecord) bool {
+	var deat_date, bap_date time.Time
+	deat_date = eventDate(person, "DEAT")
+	if deat_date.IsZero() {
+		return true
+	}
+	bap_date = eventDate(person, "BAP")
+	if bap_date.IsZero() {
+		return true
+	}
+	if deat_date.Sub(bap_date) < 0 {
+		fmt.Println(deat_date, bap_date)
+		return false
+	}
+
+	return true
+}
+
+func rule_I105(person *gedcom.IndividualRecord) bool {
+	var bap_date, buri_date time.Time
+	bap_date = eventDate(person, "BAP")
+	if bap_date.IsZero() {
+		return true
+	}
+	buri_date = eventDate(person, "BURI")
+	if buri_date.IsZero() {
+		return true
+	}
+	if bap_date.Sub(buri_date) < 0 {
+		fmt.Println(buri_date, bap_date)
+		return false
+	}
+
+	return true
+}
+
+func rule_I106(person *gedcom.IndividualRecord) bool {
+	var buri_date, deat_date time.Time
+	deat_date = eventDate(person, "DEAT")
+	if deat_date.IsZero() {
+		return true
+	}
+	buri_date = eventDate(person, "BURI")
+	if buri_date.IsZero() {
+		return true
+	}
+	if deat_date.Sub(buri_date) > 0 {
+		fmt.Println(buri_date, deat_date)
+		return false
+	}
+
+	return true
+}
+
+func rule_I107(person *gedcom.IndividualRecord) bool {
+	var bap_date, birt_date time.Time
+	bap_date = eventDate(person, "BAP")
+	if bap_date.IsZero() {
+		return true
+	}
+	birt_date = eventDate(person, "BIRT")
+	if birt_date.IsZero() {
+		return true
+	}
+	if bap_date.Sub(birt_date) >= 1 {
+		fmt.Println(bap_date, birt_date)
+		return false
+	}
+
+	return true
+}
+
+func rule_I108(person *gedcom.IndividualRecord) bool {
+	var deat_date, buri_date time.Time
+	deat_date = eventDate(person, "DEAT")
+	if deat_date.IsZero() {
+		return true
+	}
+	buri_date = eventDate(person, "BURI")
+	if buri_date.IsZero() {
+		return true
+	}
+	if deat_date.Sub(buri_date) >= 1 {
+		fmt.Println(deat_date, buri_date)
+		return false
+	}
+
+	return true
+}
+
+func rule_I109(person *gedcom.IndividualRecord) bool {
+	if person.Sex == "" {
+		return false
+	}
+
+	return true
+}
+
+func rule_I110(person *gedcom.IndividualRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_I111(person *gedcom.IndividualRecord) bool {
+	if len(person.Parents) > 1 {
+		return false
+	}
+
+	return true
+}
+
+func rule_I112(person *gedcom.IndividualRecord) bool {
+	if len(person.Family) == 0 {
+		return false
+	}
+
+	return true
+}
+
+func rule_F100(family *gedcom.FamilyRecord) bool {
+	if family.Husband == nil {
+		return false
+	}
+	if family.Wife == nil {
+		return false
+	}
+	if len(family.Child) == 0 {
+		return false
+	}
+
+	return true
+}
+
+func rule_F101(family *gedcom.FamilyRecord) bool {
+	if family.Husband == nil {
+		return false
+	}
+	if family.Wife == nil {
+		return false
+	}
+
+	return true
+}
+
+func rule_F102(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_F103(family *gedcom.FamilyRecord) bool {
+	if family.Husband == nil {
+		return false
+	}
+
+	return true
+}
+
+func rule_F104(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_F105(family *gedcom.FamilyRecord) bool {
+	if family.Wife == nil {
+		return false
+	}
+
+	return true
+}
+
+func rule_F106(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_F107(family *gedcom.FamilyRecord) bool {
+	if len(family.Child) == 0 {
+		return false
+	}
+
+	return true
+}
+
+func rule_F108(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_F109(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_F110(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M100(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M101(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M102(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M103(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M104(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M105(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M106(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M107(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M108(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M109(family *gedcom.FamilyRecord) bool {
+	if family.Husband.Sex == family.Wife.Sex {
+		return false
+	}
+
+	return true
+}
+
+func rule_M110(family *gedcom.FamilyRecord) bool {
+	if family.Husband.Sex == "F" {
+		return false
+	}
+
+	return true
+}
+
+func rule_M111(family *gedcom.FamilyRecord) bool {
+	if family.Wife.Sex == "M" {
+		return false
+	}
+
+	return true
+}
+
+func rule_M112(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M113(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M114(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_M115(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_P100(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_P101(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_P102(family *gedcom.FamilyRecord) bool {
+	var mother_birt_date, child_birt_date time.Time
+	mother_birt_date = eventDate(family.Wife, "BIRT")
+	for _, child := range family.Child {
+		child_birt_date = eventDate(child, "BIRT")
+		if mother_birt_date.Sub(child_birt_date) < 0 {
+			return false
+		}
+	}
+
+	return true
+}
+
+func rule_P103(family *gedcom.FamilyRecord) bool {
+	var mother_birt_date, child_birt_date time.Time
+	mother_birt_date = eventDate(family.Wife, "BIRT")
+	if mother_birt_date.IsZero() {
+		return true
+	}
+	for _, child := range family.Child {
+		child_birt_date = eventDate(child, "BIRT")
+		if child_birt_date.IsZero() {
+			continue
+		}
+		if mother_birt_date.Sub(child_birt_date) < YNGMOM {
+			fmt.Println(mother_birt_date, child_birt_date)
+			return false
+		}
+	}
+
+	return true
+}
+
+func rule_P104(family *gedcom.FamilyRecord) bool {
+	var mother_deat_date, child_birt_date time.Time
+	mother_deat_date = eventDate(family.Wife, "DEAT")
+	if mother_deat_date.IsZero() {
+		return true
+	}
+	for _, child := range family.Child {
+		child_birt_date = eventDate(child, "BIRT")
+		if child_birt_date.IsZero() {
+			continue
+		}
+		if mother_deat_date == child_birt_date {
+			fmt.Println(mother_deat_date, child_birt_date)
+			return false
+		}
+	}
+
+	return true
+}
+
+func rule_P105(family *gedcom.FamilyRecord) bool {
+	var father_deat_date, child_birt_date time.Time
+	father_deat_date = eventDate(family.Husband, "DEAT")
+	if father_deat_date.IsZero() {
+		return true
+	}
+	for _, child := range family.Child {
+		child_birt_date = eventDate(child, "BIRT")
+		if child_birt_date.IsZero() {
+			continue
+		}
+		if father_deat_date == child_birt_date {
+			fmt.Println(father_deat_date, child_birt_date)
+			return false
+		}
+	}
+
+	return true
+}
+
+func rule_P106(family *gedcom.FamilyRecord) bool {
+	var father_surname []string
+	var child_surname []string
+	surname_re := regexp.MustCompile(`.+ /(.+)/`)
+	for _, name := range family.Husband.Name {
+		surname := surname_re.FindStringSubmatch(name.Name)
+		if surname[1] != "" {
+			father_surname = append(father_surname, surname[1])
+		}
+	}
+	for _, child := range family.Child {
+		for _, name := range child.Name {
+			surname := surname_re.FindStringSubmatch(name.Name)
+			if surname[1] != "" {
+				child_surname = append(child_surname, surname[1])
+			}
+		}
+	}
+
+	for _, surname := range child_surname {
+		if !contains(father_surname, surname) {
+			fmt.Println(father_surname, surname)
+			return false
+		}
+	}
+
+	return true
+}
+
+func rule_C100(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_C101(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_C102(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_C103(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_C104(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func rule_C105(family *gedcom.FamilyRecord) bool {
+	/* FIXME */
+	return true
+}
+
+func eventDate(person *gedcom.IndividualRecord, eventTag string) time.Time {
+	var date time.Time
+	for _, event := range person.Event {
+		if event.Tag == eventTag {
+			date, _ = parse_date_string(event.Date)
+		}
+	}
+
+	return date
+}
+
+func parse_date_string(date string) (time.Time, error) {
+	// "2006-01-02T15:04:05.000Z"
+	layout := "02 JAN 2006"
+	t, err := time.Parse(layout, date)
+	if err == nil {
+		return t, nil
+	}
+
+	return time.Time{}, err
+}
+
+func contains(s []string, e string) bool {
+	for _, a := range s {
+		if a == e {
+			return true
+		}
+	}
+	return false
+}
+
+func PrintIndividualRecord(record *gedcom.IndividualRecord) {
+	fmt.Printf("Person (%s)", record.Xref)
+	if len(record.Name) > 0 {
+		fmt.Printf(" %s\n", record.Name[0].Name)
+	} else {
+		fmt.Printf("\n")
+	}
+}
+
+func PrintFamilyRecord(record *gedcom.FamilyRecord) {
+	fmt.Printf("Family (%s)\n", record.Xref)
+}
+
+func main() {
+	/*
+		persons born after they were married
+		persons born after their children
+		persons having similarly named children
+		persons having an ancestral loop
+
+		Persons Born After One of Their Parents Died
+		Persons Born After One of Their Parents Was Buried
+		Persons Baptized After Being Buried
+		Persons Baptized After Being Married
+		Persons Married After Being Buried
+		Persons Baptized After They Died
+		Persons Married After They Died
+		Persons Died Before Children Were Born
+		Persons Died After Being Buried
+		Persons Buried Before Having Children
+		Persons Baptized Past the Age of 5
+		Persons Married Before the Age of 14
+		Wives Married After the Age of 50
+		Persons Living Past the Age of 100
+		Persons at Least 30 Years Older Than Their Spouse
+		Persons Having a Degree of Kinship of 4 or Less
+		Persons Having a Non-biological Parent
+		Persons Whose Birth Dates Could Not Be Estimated
+		Persons with Invalid Dates
+		Families Having Swapped Spouses
+	*/
+
+	// rules are borrowed from gigatrees.com and lifelines
+	person_rules := map[string]fn_person{
+		"EI100: person's age at death is older than _oldage_": rule_I100,
+		"EI101: person is baptized before birth":              rule_I101,
+		"EI102: person dies before birth":                     rule_I102,
+		"EI103: person is buried before birth":                rule_I103,
+		"EI104: person dies before baptism":                   rule_I104,
+		"EI105: person is buried before baptism":              rule_I105,
+		"EI106: person is buried before death":                rule_I106,
+		"EI107: person is baptised after birth year":          rule_I107,
+		"EI108: person is buried after death year":            rule_I108,
+		"EI109: person has unkown gender":                     rule_I109,
+		"EI110: person has ambiguous gender":                  rule_I110,
+		"EI111: person has multiple parentage":                rule_I111,
+		"EI112: person has no family pointers":                rule_I112,
+	}
+
+	family_rules := map[string]fn_family{
+		"EF100: family has no members":             rule_F100,
+		"EF101: family has no parents":             rule_F101,
+		"EF102: husband missing pointer to family": rule_F102,
+		"EF103: family missing pointer to husband": rule_F103,
+		"EF104: wife missing pointer to family":    rule_F104,
+		"EF105: family missing pointer to wife":    rule_F105,
+		"EF106: child missing pointer to family":   rule_F106,
+		"EF107: family missing pointer to child":   rule_F107,
+		"EF108: family has multiple husbands":      rule_F108,
+		"EF109: family has multiple wives":         rule_F109,
+		"EF110: child is in family multiple times": rule_F110,
+		// marriage rules
+		"EM100: person marries before birth":                                            rule_M100,
+		"EM101: person marries after death":                                             rule_M101,
+		"EM102: person has more than WEDDER spouses":                                    rule_M102,
+		"EM103: person marries someone more than MAYDEC years older":                    rule_M103,
+		"EM104: person marries younger than YNGMAR":                                     rule_M104,
+		"EM105: person marries older than OLDMAR":                                       rule_M105,
+		"EM106: marriage out of order":                                                  rule_M106,
+		"EM107: marriage before birth from previous marriage":                           rule_M107,
+		"EM108: marriage after birth from subsequent marriage":                          rule_M108,
+		"EM109: homosexual marriage":                                                    rule_M109,
+		"EM110: person is a female husband":                                             rule_M110,
+		"EM111: person is a male wife":                                                  rule_M111,
+		"EM112: person was a widow(er) longer than LNGWDW years":                        rule_M112,
+		"EM113: person lived more than OLDUNM years and never married":                  rule_M113,
+		"EM114: person has multiple marriages, this one with no spouse and no children": rule_M114,
+		"EM115: person has same surname as spouse":                                      rule_M115,
+		// parentage rules
+		"EP100: mother has more than FECMOM children":                    rule_P100,
+		"EP101: mother is older than OLDMOM at time of birth of child":   rule_P101,
+		"EP102: child is born before mother":                             rule_P102,
+		"EP103: mother is younger than YNGMOM at time of birth of child": rule_P103,
+		"EP104: mother is dead at birth of child":                        rule_P104,
+		"EP105: father is dead at birth of child":                        rule_P105,
+		"EP106: child doesn't inherit father's surname":                  rule_P106,
+		// children rules
+		"EC100: child is born out of order with respect to a previous child": rule_C100,
+		"EC101: child is born in the same year as a previous child":          rule_C101,
+		"EC102: child is born more than CSPACE years after previous child":   rule_C102,
+		"EC103: children's births span more than CBSPAN years":               rule_C103,
+		"EC104: child is born before parent's marriage":                      rule_C104,
+		"EC105: child has same given name as sibling":                        rule_C105,
+	}
+
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
+		fmt.Fprintf(os.Stderr, "\nTool for checking of GEDCOM file for errors.")
+		fmt.Fprintf(os.Stderr, "\n\nFlags:\n")
+		flag.PrintDefaults()
+	}
+
+	var gedfile = flag.String("file", "", "GEDCOM filename")
+	var ignorelist = flag.String("ignore", "", "rules ignore list")
+	var verbose = flag.Bool("verbose", false, "verbose mode")
+
+	flag.Parse()
+
+	if *gedfile == "" {
+		flag.Usage()
+		os.Exit(1)
+	}
+
+	var ignores []string
+	if len(*ignorelist) != 0 {
+		ignores = strings.Split(*ignorelist, ",")
+		fmt.Println("These rules are ignored:", ignores)
+	}
+
+	data, _ := ioutil.ReadFile(*gedfile)
+	d := gedcom.NewDecoder(bytes.NewReader(data))
+	g, _ := d.Decode()
+
+	if *verbose {
+		fmt.Printf("Found %d persons:\n\n", len(g.Individual))
+	}
+
+	found_errors := false
+	for _, record := range g.Individual {
+		for err_msg, fn := range person_rules {
+			if contains(ignores, err_msg) {
+				continue
+			}
+			if !fn(record) {
+				PrintIndividualRecord(record)
+				fmt.Println(err_msg)
+				found_errors = true
+			}
+		}
+	}
+
+	if *verbose {
+		fmt.Printf("\nFound %d families:\n\n", len(g.Family))
+	}
+	for _, record := range g.Family {
+		for err_msg, fn := range family_rules {
+			if contains(ignores, err_msg) {
+				continue
+			}
+			if !fn(record) {
+				PrintFamilyRecord(record)
+				fmt.Println(err_msg)
+				found_errors = true
+			}
+		}
+	}
+
+	if found_errors {
+		os.Exit(1)
+	}
+}
blob - /dev/null
blob + 7f3a50bfb07fd425b03931ab8817e87b282009bf (mode 644)
--- /dev/null
+++ cmd/gedcom2sql/gedcom2sql.go
@@ -0,0 +1,194 @@
+package main
+
+import (
+	"bytes"
+	"database/sql"
+	"flag"
+	"fmt"
+	"github.com/iand/gedcom"
+	_ "github.com/mattn/go-sqlite3"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+func checkErr(err error) {
+	if err != nil {
+		panic(err)
+	}
+}
+
+const (
+	sql_table = `
+        DROP TABLE IF EXISTS famchild;
+		CREATE TABLE famchild (
+		   famID varchar(40) NOT NULL DEFAULT '',
+		   child varchar(40) NOT NULL DEFAULT '',
+		   PRIMARY KEY (famID, child)
+		);
+
+		DROP TABLE IF EXISTS family;
+		CREATE TABLE family (
+		   famID varchar(40) NOT NULL DEFAULT '',
+		   husband varchar(40) DEFAULT NULL,
+		   wife varchar(40) DEFAULT NULL,
+		   marr_date varchar(255) DEFAULT NULL,
+		   marr_plac varchar(255) DEFAULT NULL,
+		   marr_sour varchar(255) DEFAULT NULL,
+		   marb_date varchar(255) DEFAULT NULL,
+		   marb_plac varchar(255) DEFAULT NULL,
+		   marb_sour varchar(255) DEFAULT NULL,
+		   PRIMARY KEY (famID)
+		);
+
+		DROP TABLE IF EXISTS person_st;
+		CREATE TABLE person_st (
+		   persID varchar(40) NOT NULL DEFAULT '',
+		   name varchar(255) DEFAULT NULL,
+		   vorname varchar(255) DEFAULT NULL,
+		   marname varchar(255) DEFAULT NULL,
+		   sex char(1) DEFAULT NULL,
+		   birt_date varchar(255) DEFAULT NULL,
+		   birt_plac varchar(255) DEFAULT NULL,
+		   birt_sour varchar(255) DEFAULT NULL,
+		   taufe_date varchar(255) DEFAULT NULL,
+		   taufe_plac varchar(255) DEFAULT NULL,
+		   taufe_sour varchar(255) DEFAULT NULL,
+		   deat_date varchar(255) DEFAULT NULL,
+		   deat_plac varchar(255) DEFAULT NULL,
+		   deat_sour varchar(255) DEFAULT NULL,
+		   buri_date varchar(255) DEFAULT NULL,
+		   buri_plac varchar(255) DEFAULT NULL,
+		   buri_sour varchar(255) DEFAULT NULL,
+		   occupation varchar(255) DEFAULT NULL,
+		   occu_date varchar(255) DEFAULT NULL,
+		   occu_plac varchar(255) DEFAULT NULL,
+		   occu_sour varchar(255) DEFAULT NULL,
+		   religion varchar(80) DEFAULT NULL,
+		   confi_date varchar(255) DEFAULT NULL,
+		   confi_plac varchar(255) DEFAULT NULL,
+		   confi_sour varchar(255) DEFAULT NULL,
+		   note longtext,
+		   PRIMARY KEY (persID)
+		);
+        `
+)
+
+func InitDB(filepath string) *sql.DB {
+	db, err := sql.Open("sqlite3", filepath)
+	if err != nil {
+		panic(err)
+	}
+	if db == nil {
+		panic("db nil")
+	}
+	return db
+}
+
+func CreateTable(db *sql.DB) {
+	_, err := db.Exec(sql_table)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func main() {
+
+	flag.Usage = func() {
+		fmt.Println("\ngedcom2sql is a tool for conversion of GEDCOM to SQLite.")
+		fmt.Println("\nUsage:")
+		flag.PrintDefaults()
+	}
+
+	var gedfile = flag.String("file", "", "GEDCOM filename")
+	var verbose = flag.Bool("verbose", false, "verbose mode")
+
+	flag.Parse()
+
+	if *gedfile == "" {
+		flag.Usage()
+		os.Exit(1)
+	}
+
+	/*
+			if len(g.Header.SourceSystem.Version) > 0 {
+				println(g.Header.SourceSystem.Version, g.Header.SourceSystem.Id)
+		    }
+			if len(g.Header.SourceSystem.Id) > 0 {
+				println(g.Header.SourceSystem.Id)
+		    }
+	*/
+
+	var dbname = strings.TrimSuffix(*gedfile, filepath.Ext(*gedfile)) + ".sqlite"
+	db := InitDB(dbname)
+	defer db.Close()
+	CreateTable(db)
+
+	data, _ := ioutil.ReadFile(*gedfile)
+	d := gedcom.NewDecoder(bytes.NewReader(data))
+	g, _ := d.Decode()
+
+	if *verbose {
+		fmt.Println("\nPersons (Xref, Name, Sex):")
+		fmt.Println("--------------------------")
+	}
+	println("Found persons:", len(g.Individual))
+	for _, rec := range g.Individual {
+		if len(rec.Name) > 0 {
+			if *verbose {
+				fmt.Printf("%s, %s, %s\n", rec.Xref, rec.Name[0].Name, rec.Sex)
+			}
+			var birt_date, deat_date, buri_date string
+			var birt_plac, deat_plac, buri_plac string
+			for _, event := range rec.Event {
+				if *verbose {
+					fmt.Printf("\t%s, %s, %s\n", event.Tag, event.Date, event.Place.Name)
+				}
+				if event.Tag == "BIRT" {
+					birt_date = event.Date
+					birt_plac = event.Place.Name
+				}
+				if event.Tag == "DEAT" {
+					deat_date = event.Date
+					deat_plac = event.Place.Name
+				}
+				if event.Tag == "DEAT" {
+					buri_date = event.Date
+					buri_plac = event.Place.Name
+				}
+			}
+			stmt, err := db.Prepare("INSERT INTO person_st (persID, name, birt_date, birt_plac, deat_date, deat_plac, buri_date, buri_plac, sex) values(?, ?, ?, ?, ?, ?, ?, ?, ?)")
+			checkErr(err)
+			_, err = stmt.Exec(rec.Xref, rec.Name[0].Name, birt_date, birt_plac, deat_date, deat_plac, buri_date, buri_plac, rec.Sex)
+			checkErr(err)
+		}
+	}
+
+	if *verbose {
+		fmt.Println("\nFamilies (Xref, Husband, Wife):")
+		fmt.Println("-------------------------------")
+	}
+	println("Found families:", len(g.Family))
+	for _, rec := range g.Family {
+		if *verbose {
+			fmt.Printf("%s\n", rec.Xref)
+			fmt.Printf("%s, %s, %s\n", rec.Xref, rec.Husband.Xref, rec.Wife.Xref)
+		}
+		stmt, err := db.Prepare("INSERT INTO family (famID, husband, wife) values(?, ?, ?)")
+		checkErr(err)
+		_, err = stmt.Exec(rec.Xref, rec.Husband.Xref, rec.Wife.Xref)
+		checkErr(err)
+
+		for _, child := range rec.Child {
+			if *verbose {
+				fmt.Printf("\t%s, %s\n", rec.Xref, child.Xref)
+			}
+			stmt, err := db.Prepare("INSERT INTO famchild (famID, child) values(?, ?)")
+			checkErr(err)
+			_, err = stmt.Exec(rec.Xref, child.Xref)
+			checkErr(err)
+		}
+	}
+	println("Database path:", dbname)
+}
blob - 7f3a50bfb07fd425b03931ab8817e87b282009bf (mode 644)
blob + /dev/null
--- cmd/gedcom2sql/main.go
+++ /dev/null
@@ -1,194 +0,0 @@
-package main
-
-import (
-	"bytes"
-	"database/sql"
-	"flag"
-	"fmt"
-	"github.com/iand/gedcom"
-	_ "github.com/mattn/go-sqlite3"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"strings"
-)
-
-func checkErr(err error) {
-	if err != nil {
-		panic(err)
-	}
-}
-
-const (
-	sql_table = `
-        DROP TABLE IF EXISTS famchild;
-		CREATE TABLE famchild (
-		   famID varchar(40) NOT NULL DEFAULT '',
-		   child varchar(40) NOT NULL DEFAULT '',
-		   PRIMARY KEY (famID, child)
-		);
-
-		DROP TABLE IF EXISTS family;
-		CREATE TABLE family (
-		   famID varchar(40) NOT NULL DEFAULT '',
-		   husband varchar(40) DEFAULT NULL,
-		   wife varchar(40) DEFAULT NULL,
-		   marr_date varchar(255) DEFAULT NULL,
-		   marr_plac varchar(255) DEFAULT NULL,
-		   marr_sour varchar(255) DEFAULT NULL,
-		   marb_date varchar(255) DEFAULT NULL,
-		   marb_plac varchar(255) DEFAULT NULL,
-		   marb_sour varchar(255) DEFAULT NULL,
-		   PRIMARY KEY (famID)
-		);
-
-		DROP TABLE IF EXISTS person_st;
-		CREATE TABLE person_st (
-		   persID varchar(40) NOT NULL DEFAULT '',
-		   name varchar(255) DEFAULT NULL,
-		   vorname varchar(255) DEFAULT NULL,
-		   marname varchar(255) DEFAULT NULL,
-		   sex char(1) DEFAULT NULL,
-		   birt_date varchar(255) DEFAULT NULL,
-		   birt_plac varchar(255) DEFAULT NULL,
-		   birt_sour varchar(255) DEFAULT NULL,
-		   taufe_date varchar(255) DEFAULT NULL,
-		   taufe_plac varchar(255) DEFAULT NULL,
-		   taufe_sour varchar(255) DEFAULT NULL,
-		   deat_date varchar(255) DEFAULT NULL,
-		   deat_plac varchar(255) DEFAULT NULL,
-		   deat_sour varchar(255) DEFAULT NULL,
-		   buri_date varchar(255) DEFAULT NULL,
-		   buri_plac varchar(255) DEFAULT NULL,
-		   buri_sour varchar(255) DEFAULT NULL,
-		   occupation varchar(255) DEFAULT NULL,
-		   occu_date varchar(255) DEFAULT NULL,
-		   occu_plac varchar(255) DEFAULT NULL,
-		   occu_sour varchar(255) DEFAULT NULL,
-		   religion varchar(80) DEFAULT NULL,
-		   confi_date varchar(255) DEFAULT NULL,
-		   confi_plac varchar(255) DEFAULT NULL,
-		   confi_sour varchar(255) DEFAULT NULL,
-		   note longtext,
-		   PRIMARY KEY (persID)
-		);
-        `
-)
-
-func InitDB(filepath string) *sql.DB {
-	db, err := sql.Open("sqlite3", filepath)
-	if err != nil {
-		panic(err)
-	}
-	if db == nil {
-		panic("db nil")
-	}
-	return db
-}
-
-func CreateTable(db *sql.DB) {
-	_, err := db.Exec(sql_table)
-	if err != nil {
-		panic(err)
-	}
-}
-
-func main() {
-
-	flag.Usage = func() {
-		fmt.Println("\ngedcom2sql is a tool for conversion of GEDCOM to SQLite.")
-		fmt.Println("\nUsage:")
-		flag.PrintDefaults()
-	}
-
-	var gedfile = flag.String("file", "", "GEDCOM filename")
-	var verbose = flag.Bool("verbose", false, "verbose mode")
-
-	flag.Parse()
-
-	if *gedfile == "" {
-		flag.Usage()
-		os.Exit(1)
-	}
-
-	/*
-			if len(g.Header.SourceSystem.Version) > 0 {
-				println(g.Header.SourceSystem.Version, g.Header.SourceSystem.Id)
-		    }
-			if len(g.Header.SourceSystem.Id) > 0 {
-				println(g.Header.SourceSystem.Id)
-		    }
-	*/
-
-	var dbname = strings.TrimSuffix(*gedfile, filepath.Ext(*gedfile)) + ".sqlite"
-	db := InitDB(dbname)
-	defer db.Close()
-	CreateTable(db)
-
-	data, _ := ioutil.ReadFile(*gedfile)
-	d := gedcom.NewDecoder(bytes.NewReader(data))
-	g, _ := d.Decode()
-
-	if *verbose {
-		fmt.Println("\nPersons (Xref, Name, Sex):")
-		fmt.Println("--------------------------")
-	}
-	println("Found persons:", len(g.Individual))
-	for _, rec := range g.Individual {
-		if len(rec.Name) > 0 {
-			if *verbose {
-				fmt.Printf("%s, %s, %s\n", rec.Xref, rec.Name[0].Name, rec.Sex)
-			}
-			var birt_date, deat_date, buri_date string
-			var birt_plac, deat_plac, buri_plac string
-			for _, event := range rec.Event {
-				if *verbose {
-					fmt.Printf("\t%s, %s, %s\n", event.Tag, event.Date, event.Place.Name)
-				}
-				if event.Tag == "BIRT" {
-					birt_date = event.Date
-					birt_plac = event.Place.Name
-				}
-				if event.Tag == "DEAT" {
-					deat_date = event.Date
-					deat_plac = event.Place.Name
-				}
-				if event.Tag == "DEAT" {
-					buri_date = event.Date
-					buri_plac = event.Place.Name
-				}
-			}
-			stmt, err := db.Prepare("INSERT INTO person_st (persID, name, birt_date, birt_plac, deat_date, deat_plac, buri_date, buri_plac, sex) values(?, ?, ?, ?, ?, ?, ?, ?, ?)")
-			checkErr(err)
-			_, err = stmt.Exec(rec.Xref, rec.Name[0].Name, birt_date, birt_plac, deat_date, deat_plac, buri_date, buri_plac, rec.Sex)
-			checkErr(err)
-		}
-	}
-
-	if *verbose {
-		fmt.Println("\nFamilies (Xref, Husband, Wife):")
-		fmt.Println("-------------------------------")
-	}
-	println("Found families:", len(g.Family))
-	for _, rec := range g.Family {
-		if *verbose {
-			fmt.Printf("%s\n", rec.Xref)
-			fmt.Printf("%s, %s, %s\n", rec.Xref, rec.Husband.Xref, rec.Wife.Xref)
-		}
-		stmt, err := db.Prepare("INSERT INTO family (famID, husband, wife) values(?, ?, ?)")
-		checkErr(err)
-		_, err = stmt.Exec(rec.Xref, rec.Husband.Xref, rec.Wife.Xref)
-		checkErr(err)
-
-		for _, child := range rec.Child {
-			if *verbose {
-				fmt.Printf("\t%s, %s\n", rec.Xref, child.Xref)
-			}
-			stmt, err := db.Prepare("INSERT INTO famchild (famID, child) values(?, ?)")
-			checkErr(err)
-			_, err = stmt.Exec(rec.Xref, child.Xref)
-			checkErr(err)
-		}
-	}
-	println("Database path:", dbname)
-}
blob - 2eabf1fcdb2022410ae860d6658f462bb725d05a (mode 644)
blob + /dev/null
--- cmd/vis/main.go
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * timenet
- *   - http://vis.stanford.edu/papers/timenets
- *   - http://vis.berkeley.edu/courses/cs294-10-sp10/wiki/images/f/f2/Family_Tree_Visualization_-_Final_Paper.pdf
- * timeline
- *   - https://github.com/davorg/svg-timeline-genealogy
- * graphviz:
- *   - https://github.com/adrienverge/familytreemaker
- *   - https://github.com/vmiklos/ged2dot
- *   - моя реализация
- * [GEPS 030: New Visualization Techniques](https://www.gramps-project.org/wiki/index.php/GEPS_030:_New_Visualization_Techniques)
- * [Geneaquilts](https://aviz.fr/geneaquilts/)
- * https://github.com/nicolaskruchten/genealogy
- */
-
-package main
-
-import (
-	"github.com/ajstarks/svgo"
-	"os"
-)
-
-func main() {
-	width := 500
-	height := 500
-	canvas := svg.New(os.Stdout)
-	canvas.Start(width, height)
-	canvas.Circle(width/2, height/2, 100)
-	canvas.Text(width/2, height/2, "Hello, SVG", "text-anchor:middle;font-size:30px;fill:white")
-	canvas.End()
-}
blob - /dev/null
blob + 2eabf1fcdb2022410ae860d6658f462bb725d05a (mode 644)
--- /dev/null
+++ cmd/gedcom2timenet/gedcom2timenet.go
@@ -0,0 +1,31 @@
+/*
+ * timenet
+ *   - http://vis.stanford.edu/papers/timenets
+ *   - http://vis.berkeley.edu/courses/cs294-10-sp10/wiki/images/f/f2/Family_Tree_Visualization_-_Final_Paper.pdf
+ * timeline
+ *   - https://github.com/davorg/svg-timeline-genealogy
+ * graphviz:
+ *   - https://github.com/adrienverge/familytreemaker
+ *   - https://github.com/vmiklos/ged2dot
+ *   - моя реализация
+ * [GEPS 030: New Visualization Techniques](https://www.gramps-project.org/wiki/index.php/GEPS_030:_New_Visualization_Techniques)
+ * [Geneaquilts](https://aviz.fr/geneaquilts/)
+ * https://github.com/nicolaskruchten/genealogy
+ */
+
+package main
+
+import (
+	"github.com/ajstarks/svgo"
+	"os"
+)
+
+func main() {
+	width := 500
+	height := 500
+	canvas := svg.New(os.Stdout)
+	canvas.Start(width, height)
+	canvas.Circle(width/2, height/2, 100)
+	canvas.Text(width/2, height/2, "Hello, SVG", "text-anchor:middle;font-size:30px;fill:white")
+	canvas.End()
+}
blob - 1f99cfae42baf5597a80791abf7acb63e25a8163 (mode 644)
blob + /dev/null
--- cmd/vk2gedcom/main.go
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
-
-Example: ./socialtree -id 233293686
-
-TreeWalk https://gist.github.com/abhat/71332461951830f9a0f5
-https://gist.github.com/zyxar/2317744
-https://golang.org/doc/play/tree.go
-https://github.com/alonsovidales/go_graph
-
-FIXME:
-		id181554010 (зацикливается)
-		id140022470 (зацикливается)
-
-*/
-
-package main
-
-import (
-	"encoding/json"
-	"flag"
-	"fmt"
-	"io/ioutil"
-	"log"
-	"net/http"
-	"os"
-	"strconv"
-)
-
-type RawResponse struct {
-	Person []json.RawMessage `json:"response"`
-}
-
-type Person struct {
-	ID              int        `json:"id"`
-	FirstName       string     `json:"first_name"`
-	LastName        string     `json:"last_name"`
-	Sex             int        `json:"sex"`
-	Bdate           string     `json:"bdate"`
-	Photo_50        string     `json:"photo_50"`
-	Relation        int        `json:"relation"`
-	Relatives       []Relative `json:"relatives"`
-	RelationPartner RelPartner `json:"relation_partner"`
-	Generation      int
-}
-
-type RelPartner struct {
-	ID   int    `json:"id"`
-	Name string `json:"name"`
-}
-
-type Relative struct {
-	ID   int    `json:"id"`   // идентификатор пользователя
-	Type string `json:"type"` // тип родственной связи
-	Name string `json:"name"` // имя родственника
-}
-
-func ValueInSlice(n int, list []int) bool {
-	for _, i := range list {
-		if i == n {
-			return true
-		}
-	}
-	return false
-}
-
-func process_person(person Person) []Person {
-
-	var tree []Person
-	var p Person
-	if len(person.Relatives) != 0 {
-		for _, r := range person.Relatives {
-			p = Person{}
-			log.Println(r.ID, r.Type, r.Name)
-			id := strconv.Itoa(r.ID)
-			if r.ID > 0 {
-				log.Println("Person is in VK", id)
-				p = get_profile(id)
-			} else {
-				log.Println("Person is absent in VK", id)
-				p.FirstName = r.Name
-				p.LastName = r.Name
-			}
-
-			switch r.Type {
-			case "child":
-				p.Generation = person.Generation - 1
-			case "sibling":
-				p.Generation = person.Generation
-			case "parent":
-				p.Generation = person.Generation + 1
-			case "grandparent":
-				p.Generation = person.Generation - 2
-			case "grandchild":
-				p.Generation = person.Generation + 2
-			}
-			tree = append(tree, p)
-			process_person(p)
-		}
-	}
-
-	relation := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
-	if ValueInSlice(person.Relation, relation) {
-		log.Println("no spouse")
-	} else {
-		p = Person{}
-		id := strconv.Itoa(person.RelationPartner.ID)
-		p = get_profile(id)
-		tree = append(tree, p)
-		process_person(p)
-	}
-
-	return tree
-}
-
-func get_profile(id string) Person {
-
-	url := "https://api.vk.com/method/users.get?user_ids="
-	url = url + id
-	url = url + "&fields=first_name,last_name,photo_50,relatives,relation,bdate,sex&v=5.67"
-
-	res, err := http.Get(url)
-	if err != nil {
-		panic(err.Error())
-	}
-
-	body, err := ioutil.ReadAll(res.Body)
-	if err != nil {
-		panic(err.Error())
-	}
-
-	var raw RawResponse
-	err = json.Unmarshal(body, &raw)
-	if err != nil {
-		log.Fatal("Error parsing json: ", err)
-	}
-
-	var person Person
-	err = json.Unmarshal(raw.Person[0], &person)
-	if err != nil {
-		log.Fatal("Error parsing json: ", err)
-	}
-
-	var sex string
-	switch person.Sex {
-	case 1:
-		sex = "Ж"
-	case 2:
-		sex = "М"
-	default:
-		sex = "неизвестно"
-	}
-
-	log.Println(person.FirstName, person.LastName, sex, person.Bdate)
-	log.Println("\t", person.Relatives)
-
-	return person
-}
-
-func main() {
-
-	flag.Usage = func() {
-		fmt.Println("\nsocialtree is a tool for getting relations via VK.")
-		fmt.Println("\nUsage:")
-		flag.PrintDefaults()
-	}
-
-	var person_id = flag.String("id", "", "person id")
-
-	flag.Parse()
-
-	if *person_id == "" {
-		flag.Usage()
-		os.Exit(1)
-	}
-
-	var Tree []Person
-
-	person := get_profile(*person_id)
-	person.Generation = 0
-	Tree = append(Tree, person)
-	Tree = append(Tree, process_person(person)...) // new
-
-	/*
-		var p Person
-		if len(person.Relatives) != 0 {
-			for _, r := range person.Relatives {
-				p = Person{}
-				log.Println(r.ID, r.Type, r.Name)
-				id := strconv.Itoa(r.ID)
-				if r.ID > 0 {
-					log.Println("Person is in VK", id)
-					p = get_profile(id)
-				} else {
-					log.Println("Person is absent in VK", id)
-					p.FirstName = r.Name
-					p.LastName = r.Name
-				}
-
-				switch r.Type {
-				case "child":
-					p.Generation = person.Generation - 1
-				case "sibling":
-					p.Generation = person.Generation
-				case "parent":
-					p.Generation = person.Generation + 1
-				case "grandparent":
-					p.Generation = person.Generation - 2
-				case "grandchild":
-					p.Generation = person.Generation + 2
-				}
-				Tree = append(Tree, p)
-				process_person(p)
-			}
-		}
-
-		relation := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
-		if ValueInSlice(person.Relation, relation) {
-			log.Println("no spouse")
-		} else {
-			p = Person{}
-			id := strconv.Itoa(person.RelationPartner.ID)
-			p = get_profile(id)
-			Tree = append(Tree, p)
-			process_person(p)
-		}
-	*/
-
-	prettyJSON, err := json.MarshalIndent(Tree, "", "  ")
-	if err != nil {
-		panic(err)
-	}
-	log.Println(string(prettyJSON))
-}
blob - /dev/null
blob + 1f99cfae42baf5597a80791abf7acb63e25a8163 (mode 644)
--- /dev/null
+++ cmd/vk2gedcom/vk2gedcom.go
@@ -0,0 +1,233 @@
+/*
+
+Example: ./socialtree -id 233293686
+
+TreeWalk https://gist.github.com/abhat/71332461951830f9a0f5
+https://gist.github.com/zyxar/2317744
+https://golang.org/doc/play/tree.go
+https://github.com/alonsovidales/go_graph
+
+FIXME:
+		id181554010 (зацикливается)
+		id140022470 (зацикливается)
+
+*/
+
+package main
+
+import (
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"strconv"
+)
+
+type RawResponse struct {
+	Person []json.RawMessage `json:"response"`
+}
+
+type Person struct {
+	ID              int        `json:"id"`
+	FirstName       string     `json:"first_name"`
+	LastName        string     `json:"last_name"`
+	Sex             int        `json:"sex"`
+	Bdate           string     `json:"bdate"`
+	Photo_50        string     `json:"photo_50"`
+	Relation        int        `json:"relation"`
+	Relatives       []Relative `json:"relatives"`
+	RelationPartner RelPartner `json:"relation_partner"`
+	Generation      int
+}
+
+type RelPartner struct {
+	ID   int    `json:"id"`
+	Name string `json:"name"`
+}
+
+type Relative struct {
+	ID   int    `json:"id"`   // идентификатор пользователя
+	Type string `json:"type"` // тип родственной связи
+	Name string `json:"name"` // имя родственника
+}
+
+func ValueInSlice(n int, list []int) bool {
+	for _, i := range list {
+		if i == n {
+			return true
+		}
+	}
+	return false
+}
+
+func process_person(person Person) []Person {
+
+	var tree []Person
+	var p Person
+	if len(person.Relatives) != 0 {
+		for _, r := range person.Relatives {
+			p = Person{}
+			log.Println(r.ID, r.Type, r.Name)
+			id := strconv.Itoa(r.ID)
+			if r.ID > 0 {
+				log.Println("Person is in VK", id)
+				p = get_profile(id)
+			} else {
+				log.Println("Person is absent in VK", id)
+				p.FirstName = r.Name
+				p.LastName = r.Name
+			}
+
+			switch r.Type {
+			case "child":
+				p.Generation = person.Generation - 1
+			case "sibling":
+				p.Generation = person.Generation
+			case "parent":
+				p.Generation = person.Generation + 1
+			case "grandparent":
+				p.Generation = person.Generation - 2
+			case "grandchild":
+				p.Generation = person.Generation + 2
+			}
+			tree = append(tree, p)
+			process_person(p)
+		}
+	}
+
+	relation := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
+	if ValueInSlice(person.Relation, relation) {
+		log.Println("no spouse")
+	} else {
+		p = Person{}
+		id := strconv.Itoa(person.RelationPartner.ID)
+		p = get_profile(id)
+		tree = append(tree, p)
+		process_person(p)
+	}
+
+	return tree
+}
+
+func get_profile(id string) Person {
+
+	url := "https://api.vk.com/method/users.get?user_ids="
+	url = url + id
+	url = url + "&fields=first_name,last_name,photo_50,relatives,relation,bdate,sex&v=5.67"
+
+	res, err := http.Get(url)
+	if err != nil {
+		panic(err.Error())
+	}
+
+	body, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		panic(err.Error())
+	}
+
+	var raw RawResponse
+	err = json.Unmarshal(body, &raw)
+	if err != nil {
+		log.Fatal("Error parsing json: ", err)
+	}
+
+	var person Person
+	err = json.Unmarshal(raw.Person[0], &person)
+	if err != nil {
+		log.Fatal("Error parsing json: ", err)
+	}
+
+	var sex string
+	switch person.Sex {
+	case 1:
+		sex = "Ж"
+	case 2:
+		sex = "М"
+	default:
+		sex = "неизвестно"
+	}
+
+	log.Println(person.FirstName, person.LastName, sex, person.Bdate)
+	log.Println("\t", person.Relatives)
+
+	return person
+}
+
+func main() {
+
+	flag.Usage = func() {
+		fmt.Println("\nsocialtree is a tool for getting relations via VK.")
+		fmt.Println("\nUsage:")
+		flag.PrintDefaults()
+	}
+
+	var person_id = flag.String("id", "", "person id")
+
+	flag.Parse()
+
+	if *person_id == "" {
+		flag.Usage()
+		os.Exit(1)
+	}
+
+	var Tree []Person
+
+	person := get_profile(*person_id)
+	person.Generation = 0
+	Tree = append(Tree, person)
+	Tree = append(Tree, process_person(person)...) // new
+
+	/*
+		var p Person
+		if len(person.Relatives) != 0 {
+			for _, r := range person.Relatives {
+				p = Person{}
+				log.Println(r.ID, r.Type, r.Name)
+				id := strconv.Itoa(r.ID)
+				if r.ID > 0 {
+					log.Println("Person is in VK", id)
+					p = get_profile(id)
+				} else {
+					log.Println("Person is absent in VK", id)
+					p.FirstName = r.Name
+					p.LastName = r.Name
+				}
+
+				switch r.Type {
+				case "child":
+					p.Generation = person.Generation - 1
+				case "sibling":
+					p.Generation = person.Generation
+				case "parent":
+					p.Generation = person.Generation + 1
+				case "grandparent":
+					p.Generation = person.Generation - 2
+				case "grandchild":
+					p.Generation = person.Generation + 2
+				}
+				Tree = append(Tree, p)
+				process_person(p)
+			}
+		}
+
+		relation := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
+		if ValueInSlice(person.Relation, relation) {
+			log.Println("no spouse")
+		} else {
+			p = Person{}
+			id := strconv.Itoa(person.RelationPartner.ID)
+			p = get_profile(id)
+			Tree = append(Tree, p)
+			process_person(p)
+		}
+	*/
+
+	prettyJSON, err := json.MarshalIndent(Tree, "", "  ")
+	if err != nil {
+		panic(err)
+	}
+	log.Println(string(prettyJSON))
+}