#!/usr/bin/perl # TODO - check to see children, spouses are listed in right order # build.pl - build DBM relational databases from text input file # Copyright (C) 1997 by John J. Chew, III # All Rights Reserved # @gDBMSuffixes = ('.dir', '.pag'); # depends on operating system @gDBMSuffixes = ('.db'); # depends on operating system @dbs = ('child', 'marriage', 'parent', 'person'); for $db (@dbs) { # this is going to fail for DOS users for $suffix (@gDBMSuffixes) { unlink $db . '.new' . $suffix; } eval("dbmopen($db, \$db . '.new', 0600)"); } $lastID = 0; while (<>) { # delete comments, skip blank lines next if /^\s*#/; s/\s+$//; next unless /\S/; # newline-separated records with |-separated fields @F = split(/\|/, $_, 20); # record type is determined by first field $type = shift @F; # 'p' denotes information about a person # p|id|surname|given|title|father|mother|bdate|bplace|ddate|dplace|notes if ($type eq 'p') { die "Duplicate p|$F[0] at line $.\n" if defined $person{$F[0]}; die "Wrong number of fields (".($#F+1)." instead of 11):\n$_\n" unless $#F == 10; die "Disorder: $F[0] < $lastID\n" if $F[0] < $lastID; $lastID = $F[0]; $person{$F[0]} = join('|', @F[1,2,3,6,7,8,9,10]); $parent{$F[0]} = "$F[4]|$F[5]"; for $p (@F[4..5]) { next unless length($p); if (defined $child{$p}) { $child{$p} .= "|$F[0]"; } else { $child{$p} = $F[0]; } } } # 'm' denotes information about a marriage # m|id|husband|wife|mdate|mplace|ddate|dplace # (I suppose for same-sex marriages it's just m|spouse|spouse|...) elsif ($type eq 'm') { die "Wrong number of fields (".($#F+1)." instead of 6):\n$_\n" unless $#F == 5; for $i (0..1) { $p = @F[$i]; next unless length($p); $s = $F[1-$i]; if (defined $marriage{$p}) { $marriage{$p} .= "\n" . join('|', $s, @F[2..$#F]); } else { $marriage{$p} = join('|', $s, @F[2..$#F]); } } } else { die "Can't parse: $_\n"; } } # rearrange children in birth order while (my ($id, $kids) = each %child) { $child{$id} = join('|', sort { my $aa = (split(/\|/, $person{$a}))[3]; my $bb = (split(/\|/, $person{$b}))[3]; $aa =~ s/^\D+//; $bb =~ s/^\D+//; $aa cmp $bb or $a cmp $b; } split(/\|/, $kids)); } # calculate ancestor depths sub acalc { local($p) = @_; local($count, $depth, $parent, @parent, $pcount, $pdepth); if (defined $adata{$p}) { split(/\|/, $adata{$p}); } else { $count = $depth = 0; @parent = split(/\|/, $parent{$p}); $adata{$p} = '0|999999'; for $parent (@parent) { if ($parent) { ($pcount, $pdepth) = &acalc($parent); $count += $pcount + 1; $depth = $pdepth + 1 if $depth <= $pdepth; } } $adata{$p} = "$count|$depth"; ($count, $depth); } } # first version breaks with some versions of db and perl # %adata = (); while (($p) = each %parent) { &acalc($p); } %adata = (); for my $p (keys %parent) { &acalc($p); } # calculate descendant depths sub dcalc { my($p) = @_; my($count, $depth, $child, @child, $ccount, $cdepth); # print "dcalc($p).\n" if $p > 70000; if (defined $ddata{$p}) { split(/\|/, $ddata{$p}); } else { $count = $depth = 0; @child = split(/\|/, $child{$p}); $ddata{$p} = '0|999999'; for $child (@child) { ($ccount, $cdepth) = &dcalc($child); $count += $ccount + 1; $depth = $cdepth + 1 if $depth <= $cdepth; } $ddata{$p} = "$count|$depth"; ($count, $depth); } } # first version breaks with some versions of db and perl # %ddata = (); while (($p) = each %child) { &dcalc($p); } %ddata = (); for my $p (keys %child) { &dcalc($p); } # store ancestor and descendant depths for $p (keys %person) { $person{$p} = $person{$p} . '|' . ($adata{$p} || '0|0') . '|' . ($ddata{$p} || '0|0'); } for $db (@dbs) { eval("dbmclose($db)"); for $suffix (@gDBMSuffixes) { unlink $db . $suffix; } for $suffix (@gDBMSuffixes) { rename($db . '.new' . $suffix, $db . $suffix); } }