| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# Copyright 1998-2019, Paul Johnson (paul@pjcj.net) |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# This software is free. It is licensed under the same terms as Perl itself. |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# The latest version of this software should be available from my homepage: |
|
6
|
|
|
|
|
|
|
# http://www.pjcj.net |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# documentation at __END__ |
|
9
|
|
|
|
|
|
|
|
|
10
|
11
|
|
|
11
|
|
58
|
use strict; |
|
|
11
|
|
|
|
|
18
|
|
|
|
11
|
|
|
|
|
418
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
require 5.005; |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
package Gedcom::Record; |
|
15
|
|
|
|
|
|
|
|
|
16
|
11
|
|
|
11
|
|
48
|
use vars qw($VERSION @ISA $AUTOLOAD); |
|
|
11
|
|
|
|
|
12
|
|
|
|
11
|
|
|
|
|
658
|
|
|
17
|
|
|
|
|
|
|
$VERSION = "1.21"; |
|
18
|
|
|
|
|
|
|
@ISA = qw( Gedcom::Item ); |
|
19
|
|
|
|
|
|
|
|
|
20
|
11
|
|
|
11
|
|
58
|
use Carp; |
|
|
11
|
|
|
|
|
22
|
|
|
|
11
|
|
|
|
|
592
|
|
|
21
|
11
|
|
|
11
|
|
666
|
BEGIN { eval "use Date::Manip" } # We'll use this if it is available |
|
|
11
|
|
|
11
|
|
4113
|
|
|
|
11
|
|
|
|
|
1312154
|
|
|
|
11
|
|
|
|
|
1458
|
|
|
22
|
|
|
|
|
|
|
|
|
23
|
11
|
|
|
11
|
|
92
|
use Gedcom::Item 1.21; |
|
|
11
|
|
|
|
|
179
|
|
|
|
11
|
|
|
|
|
265
|
|
|
24
|
11
|
|
|
11
|
|
6479
|
use Gedcom::Comparison 1.21; |
|
|
11
|
|
|
|
|
133
|
|
|
|
11
|
|
|
|
|
362
|
|
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
BEGIN |
|
27
|
|
|
|
|
|
|
{ |
|
28
|
11
|
|
|
11
|
|
4835
|
use subs keys %Gedcom::Funcs; |
|
|
11
|
|
|
|
|
222
|
|
|
|
11
|
|
|
|
|
894
|
|
|
29
|
11
|
|
|
11
|
|
20978
|
*tag_record = \&Gedcom::Item::get_item; |
|
30
|
11
|
|
|
|
|
27
|
*delete_record = \&Gedcom::Item::delete_item; |
|
31
|
11
|
|
|
|
|
1174
|
*get_record = \&record; |
|
32
|
|
|
|
|
|
|
} |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
0
|
|
|
sub DESTROY {} |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub AUTOLOAD { |
|
37
|
26
|
|
|
26
|
|
254
|
my ($self) = @_; # don't change @_ because of the goto |
|
38
|
26
|
|
|
|
|
49
|
my $func = $AUTOLOAD; |
|
39
|
|
|
|
|
|
|
# print "autoloading $func\n"; |
|
40
|
26
|
|
|
|
|
153
|
$func =~ s/^.*:://; |
|
41
|
26
|
50
|
|
|
|
128
|
carp "Undefined subroutine $func called" unless $Gedcom::Funcs{lc $func}; |
|
42
|
11
|
|
|
11
|
|
71
|
no strict "refs"; |
|
|
11
|
|
|
|
|
26
|
|
|
|
11
|
|
|
|
|
35533
|
|
|
43
|
|
|
|
|
|
|
*$func = sub { |
|
44
|
3113
|
|
|
3113
|
|
41373
|
my $self = shift; |
|
45
|
3113
|
|
|
|
|
3639
|
my ($count) = @_; |
|
46
|
3113
|
|
|
|
|
2999
|
my $v; |
|
47
|
|
|
|
|
|
|
# print "[[ $func ]]\n"; |
|
48
|
3113
|
100
|
|
|
|
3751
|
if (wantarray) { |
|
49
|
|
|
|
|
|
|
return map { |
|
50
|
3106
|
|
|
|
|
5722
|
$_ && |
|
51
|
3091
|
100
|
66
|
|
|
4390
|
do { $v = $_->full_value; defined $v && length $v ? $v : $_ } |
|
|
3091
|
50
|
|
|
|
7295
|
|
|
|
3091
|
|
|
|
|
14748
|
|
|
52
|
|
|
|
|
|
|
} $self->record([$func, $count]); |
|
53
|
|
|
|
|
|
|
} else { |
|
54
|
7
|
|
|
|
|
41
|
my $r = $self->record([$func, $count]); |
|
55
|
|
|
|
|
|
|
return $r && |
|
56
|
7
|
|
33
|
|
|
36
|
do { $v = $r->full_value; defined $v && length $v ? $v : $r } |
|
57
|
|
|
|
|
|
|
} |
|
58
|
26
|
|
|
|
|
390
|
}; |
|
59
|
26
|
|
|
|
|
117
|
goto &$func |
|
60
|
|
|
|
|
|
|
} |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub record { |
|
63
|
3219
|
|
|
3219
|
1
|
4629
|
my $self = shift; |
|
64
|
3219
|
|
|
|
|
3687
|
my @records = ($self); |
|
65
|
3219
|
100
|
|
|
|
3878
|
for my $func (map { ref() ? $_ : split } @_) { |
|
|
3314
|
|
|
|
|
6414
|
|
|
66
|
3322
|
|
|
|
|
3518
|
my $count = 0; |
|
67
|
3322
|
100
|
|
|
|
6494
|
($func, $count) = @$func if ref $func eq "ARRAY"; |
|
68
|
3322
|
50
|
|
|
|
5659
|
if (ref $func) { |
|
69
|
0
|
|
|
|
|
0
|
warn "Invalid record of type ", ref $func, " requested"; |
|
70
|
0
|
|
|
|
|
0
|
return undef; |
|
71
|
|
|
|
|
|
|
} |
|
72
|
3322
|
|
|
|
|
4404
|
my $record = $Gedcom::Funcs{lc $func}; |
|
73
|
3322
|
50
|
|
|
|
4278
|
unless ($record) { |
|
74
|
0
|
0
|
|
|
|
0
|
warn $func |
|
|
|
0
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
? "Non standard record of type $func requested" |
|
76
|
|
|
|
|
|
|
: "Record type not specified" |
|
77
|
|
|
|
|
|
|
unless $func =~ /^_/; |
|
78
|
0
|
|
|
|
|
0
|
$record = $func; |
|
79
|
|
|
|
|
|
|
} |
|
80
|
|
|
|
|
|
|
|
|
81
|
3322
|
|
|
|
|
4922
|
@records = map { $_->tag_record($record, $count) } @records; |
|
|
3315
|
|
|
|
|
5333
|
|
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# fams and famc need to be resolved |
|
84
|
3322
|
50
|
33
|
|
|
9665
|
@records = map { $self->resolve($_->{value}) } @records |
|
|
0
|
|
|
|
|
0
|
|
|
85
|
|
|
|
|
|
|
if $record eq "FAMS" || $record eq "FAMC"; |
|
86
|
|
|
|
|
|
|
} |
|
87
|
3219
|
100
|
|
|
|
5759
|
wantarray ? @records : $records[0] |
|
88
|
|
|
|
|
|
|
} |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub get_value { |
|
91
|
103
|
|
|
103
|
1
|
33404
|
my $self = shift; |
|
92
|
103
|
100
|
|
|
|
213
|
if (wantarray) { |
|
93
|
96
|
50
|
|
|
|
183
|
return map { my $v = $_->full_value; defined $v and length $v ? $v : () } |
|
|
82
|
50
|
|
|
|
154
|
|
|
|
82
|
|
|
|
|
339
|
|
|
94
|
|
|
|
|
|
|
$self->record(@_); |
|
95
|
|
|
|
|
|
|
} else { |
|
96
|
7
|
|
|
|
|
29
|
my $record = $self->record(@_); |
|
97
|
7
|
|
66
|
|
|
38
|
return $record && $record->full_value; |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
} |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
sub tag_value { |
|
102
|
62489
|
|
|
62489
|
0
|
63467
|
my $self = shift; |
|
103
|
62489
|
100
|
|
|
|
73337
|
if (wantarray) { |
|
104
|
52414
|
50
|
|
|
|
80867
|
return map { my $v = $_->full_value; defined $v and length $v ? $v : () } |
|
|
53259
|
50
|
|
|
|
81953
|
|
|
|
53259
|
|
|
|
|
179447
|
|
|
105
|
|
|
|
|
|
|
$self->tag_record(@_); |
|
106
|
|
|
|
|
|
|
} else { |
|
107
|
10075
|
|
|
|
|
15343
|
my $record = $self->tag_record(@_); |
|
108
|
10075
|
|
33
|
|
|
20304
|
return $record && $record->full_value; |
|
109
|
|
|
|
|
|
|
} |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
sub add_record { |
|
113
|
80
|
|
|
80
|
0
|
90
|
my $self = shift; |
|
114
|
80
|
|
|
|
|
173
|
my (%args) = @_; |
|
115
|
|
|
|
|
|
|
|
|
116
|
80
|
50
|
|
|
|
145
|
die "No tag specified" unless defined $args{tag}; |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
my $record = Gedcom::Record->new( |
|
119
|
|
|
|
|
|
|
gedcom => $self->{gedcom}, |
|
120
|
|
|
|
|
|
|
callback => $self->{callback}, |
|
121
|
|
|
|
|
|
|
tag => $args{tag}, |
|
122
|
80
|
|
|
|
|
178
|
); |
|
123
|
|
|
|
|
|
|
|
|
124
|
80
|
50
|
|
|
|
215
|
if (!defined $self->{grammar}) { |
|
|
|
50
|
|
|
|
|
|
|
125
|
0
|
|
|
|
|
0
|
warn "$self->{tag} has no grammar\n"; |
|
126
|
|
|
|
|
|
|
} elsif (my @g = $self->{grammar}->item($args{tag})) { |
|
127
|
|
|
|
|
|
|
# use DDS; print Dump \@g; |
|
128
|
80
|
|
|
|
|
168
|
my $grammar = $g[0]; |
|
129
|
80
|
|
|
|
|
114
|
for my $g (@g) { |
|
130
|
|
|
|
|
|
|
# print "testing $args{tag} ", $args{val} // "undef", " against ", |
|
131
|
|
|
|
|
|
|
# $g->{value} // "undef", "\n"; |
|
132
|
82
|
100
|
|
|
|
124
|
if ($args{tag} eq "NOTE") { |
|
133
|
6
|
100
|
66
|
|
|
44
|
if (( defined $args{xref} && $g->{value} =~ /xref/i) || |
|
|
|
|
66
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
134
|
|
|
|
|
|
|
(!defined $args{xref} && $g->{value} !~ /xref/i)) { |
|
135
|
|
|
|
|
|
|
# print "note match\n"; |
|
136
|
5
|
|
|
|
|
6
|
$grammar = $g; |
|
137
|
5
|
|
|
|
|
9
|
last; |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
} else { |
|
140
|
76
|
100
|
100
|
|
|
260
|
if (( defined $args{val} && $g->{value}) || |
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
141
|
|
|
|
|
|
|
(!defined $args{val} && !$g->{value})) { |
|
142
|
|
|
|
|
|
|
# print "match\n"; |
|
143
|
67
|
|
|
|
|
75
|
$grammar = $g; |
|
144
|
67
|
|
|
|
|
88
|
last; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
} |
|
148
|
80
|
|
|
|
|
158
|
$self->parse($record, $grammar); |
|
149
|
|
|
|
|
|
|
} else { |
|
150
|
0
|
|
|
|
|
0
|
warn "$args{tag} is not a sub-item of $self->{tag}\n"; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
80
|
|
|
|
|
85
|
push @{$self->{items}}, $record; |
|
|
80
|
|
|
|
|
109
|
|
|
154
|
|
|
|
|
|
|
|
|
155
|
80
|
|
|
|
|
197
|
$record |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub add { |
|
159
|
62
|
|
|
62
|
1
|
92
|
my $self = shift; |
|
160
|
62
|
|
|
|
|
81
|
my ($xref, $val); |
|
161
|
62
|
100
|
66
|
|
|
189
|
if (@_ > 1 && ref $_[-1] ne "ARRAY") { |
|
162
|
59
|
|
|
|
|
78
|
$val = pop; |
|
163
|
59
|
100
|
|
|
|
199
|
if (UNIVERSAL::isa($val, "Gedcom::Record")) { |
|
164
|
6
|
|
|
|
|
6
|
$xref = $val; |
|
165
|
6
|
|
|
|
|
7
|
$val = undef; |
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
} |
|
168
|
|
|
|
|
|
|
|
|
169
|
62
|
100
|
|
|
|
95
|
my @funcs = map { ref() ? $_ : split } @_; |
|
|
64
|
|
|
|
|
188
|
|
|
170
|
62
|
100
|
|
|
|
174
|
$funcs[-1] = [$funcs[-1], 0] unless ref $funcs[-1]; |
|
171
|
62
|
|
|
|
|
74
|
push @{$funcs[-1]}, { xref => $xref, val => $val }; |
|
|
62
|
|
|
|
|
162
|
|
|
172
|
62
|
|
|
|
|
124
|
my $record = $self->get_and_create(@funcs); |
|
173
|
|
|
|
|
|
|
|
|
174
|
62
|
100
|
|
|
|
93
|
if (defined $xref) { |
|
175
|
6
|
|
|
|
|
11
|
$record->{value} = $xref->{xref}; |
|
176
|
6
|
|
|
|
|
12
|
$self->{gedcom}{xrefs}{$xref->{xref}} = $xref; |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
|
|
179
|
62
|
100
|
|
|
|
87
|
if (defined $val) { |
|
180
|
53
|
|
|
|
|
80
|
$record->{value} = $val; |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
$record |
|
184
|
62
|
|
|
|
|
216
|
} |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub set { |
|
187
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
|
188
|
1
|
|
|
|
|
2
|
my $val = pop; |
|
189
|
|
|
|
|
|
|
|
|
190
|
1
|
50
|
|
|
|
3
|
my @funcs = map { ref() ? $_ : split } @_; |
|
|
1
|
|
|
|
|
6
|
|
|
191
|
1
|
|
|
|
|
3
|
my $r = $self->get_and_create(@funcs); |
|
192
|
|
|
|
|
|
|
|
|
193
|
1
|
50
|
|
|
|
6
|
if (UNIVERSAL::isa($val, "Gedcom::Record")) { |
|
194
|
0
|
|
|
|
|
0
|
$r->{value} = $val->{xref}; |
|
195
|
0
|
|
|
|
|
0
|
$self->{gedcom}{xrefs}{$val->{xref}} = $val; |
|
196
|
|
|
|
|
|
|
} else { |
|
197
|
1
|
|
|
|
|
3
|
$r->{value} = $val; |
|
198
|
|
|
|
|
|
|
} |
|
199
|
|
|
|
|
|
|
|
|
200
|
1
|
|
|
|
|
13
|
$r |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub get_and_create { |
|
204
|
63
|
|
|
63
|
0
|
69
|
my $self = shift; |
|
205
|
63
|
|
|
|
|
84
|
my @funcs = @_; |
|
206
|
|
|
|
|
|
|
# use DDS; print "get_and_create: " , Dump \@funcs; |
|
207
|
|
|
|
|
|
|
|
|
208
|
63
|
|
|
|
|
68
|
my $rec = $self; |
|
209
|
63
|
|
|
|
|
113
|
for my $f (0 .. $#funcs) { |
|
210
|
72
|
|
|
|
|
120
|
my ($func, $count, $args) = ($funcs[$f], 1); |
|
211
|
72
|
50
|
|
|
|
119
|
$args = {} unless defined $args; |
|
212
|
72
|
100
|
|
|
|
193
|
($func, $count, $args) = @$func if ref $func eq "ARRAY"; |
|
213
|
72
|
|
|
|
|
77
|
$count--; |
|
214
|
|
|
|
|
|
|
|
|
215
|
72
|
50
|
|
|
|
113
|
if (ref $func) { |
|
216
|
0
|
|
|
|
|
0
|
warn "Invalid record of type ", ref $func, " requested"; |
|
217
|
0
|
|
|
|
|
0
|
return undef; |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
|
|
220
|
72
|
|
|
|
|
130
|
my $record = $Gedcom::Funcs{lc $func}; |
|
221
|
72
|
50
|
|
|
|
99
|
unless ($record) { |
|
222
|
0
|
0
|
|
|
|
0
|
warn $func |
|
|
|
0
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
? "Non standard record of type $func requested" |
|
224
|
|
|
|
|
|
|
: "Record type not specified" |
|
225
|
|
|
|
|
|
|
unless $func =~ /^_/; |
|
226
|
0
|
|
|
|
|
0
|
$record = $func; |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# print "$func [$count] - $record\n"; |
|
230
|
|
|
|
|
|
|
|
|
231
|
72
|
|
|
|
|
163
|
my @records = $rec->tag_record($record); |
|
232
|
|
|
|
|
|
|
|
|
233
|
72
|
100
|
|
|
|
116
|
if ($count < 0) { |
|
|
|
100
|
|
|
|
|
|
|
234
|
61
|
|
|
|
|
161
|
$rec = $rec->add_record(tag => $record, %$args); |
|
235
|
|
|
|
|
|
|
} elsif ($#records < $count) { |
|
236
|
7
|
|
|
|
|
77
|
my $new; |
|
237
|
|
|
|
|
|
|
$new = $rec->add_record(tag => $record, %$args) |
|
238
|
7
|
|
|
|
|
56
|
for (0 .. @records - $count); |
|
239
|
7
|
|
|
|
|
15
|
$rec = $new; |
|
240
|
|
|
|
|
|
|
} else { |
|
241
|
4
|
|
|
|
|
9
|
$rec = $records[$count]; |
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
$rec |
|
246
|
63
|
|
|
|
|
82
|
} |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub parse { |
|
249
|
|
|
|
|
|
|
# print "parsing\n"; |
|
250
|
6531
|
|
|
6531
|
1
|
6640
|
my $self = shift; |
|
251
|
6531
|
|
|
|
|
8471
|
my ($record, $grammar, $test) = @_; |
|
252
|
6531
|
|
50
|
|
|
15996
|
$test ||= 0; |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# print "checking "; $record->print(); |
|
255
|
|
|
|
|
|
|
# print "against "; $grammar->print(); |
|
256
|
|
|
|
|
|
|
# print "test is $test\n"; |
|
257
|
|
|
|
|
|
|
|
|
258
|
6531
|
|
|
|
|
7697
|
my $t = $record->{tag}; |
|
259
|
6531
|
|
|
|
|
6982
|
my $g = $grammar->{tag}; |
|
260
|
6531
|
50
|
33
|
|
|
13842
|
die "Can't match $t with $g" if $t && $t ne $g; # internal error |
|
261
|
|
|
|
|
|
|
|
|
262
|
6531
|
|
|
|
|
7819
|
$record->{grammar} = $grammar; |
|
263
|
6531
|
|
|
|
|
8297
|
my $class = $record->{gedcom}{types}{$t}; |
|
264
|
6531
|
100
|
|
|
|
9630
|
bless $record, "Gedcom::$class" if $class; |
|
265
|
|
|
|
|
|
|
|
|
266
|
6531
|
|
|
|
|
6309
|
my $match = 1; |
|
267
|
|
|
|
|
|
|
|
|
268
|
6531
|
|
|
|
|
6233
|
for my $r (@{$record->{items}}) { |
|
|
6531
|
|
|
|
|
9593
|
|
|
269
|
5580
|
|
|
|
|
6627
|
my $tag = $r->{tag}; |
|
270
|
5580
|
|
|
|
|
5233
|
my @i; |
|
271
|
|
|
|
|
|
|
# print "- valid sub-items of $t: @{[keys %{$grammar->valid_items}]}\n"; |
|
272
|
5580
|
|
|
|
|
8448
|
for my $i ($grammar->item($tag)) { |
|
273
|
|
|
|
|
|
|
# Try to get rid of matches we don't want because they only match |
|
274
|
|
|
|
|
|
|
# in name. |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# Check that the level is appropriate. |
|
277
|
|
|
|
|
|
|
# print " - ", $i->level, "|", $r->level, "\n"; |
|
278
|
5590
|
50
|
33
|
|
|
8811
|
next unless $i->level =~ /^[+0]/ || $i->level == $r->level; |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# Check we have a pointer iff we need one. |
|
281
|
|
|
|
|
|
|
# print " + ", $i->value, "|", $r->value, "|", $r->pointer, "\n"; |
|
282
|
5590
|
100
|
100
|
|
|
9258
|
next if $i->value && ($i->value =~ /^pointer || 0)); |
|
|
|
|
100
|
|
|
|
|
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
# print "pushing\n"; |
|
285
|
5575
|
|
|
|
|
9184
|
push @i, $i; |
|
286
|
|
|
|
|
|
|
} |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# print "valid sub-items of $t: @{[keys %{$grammar->valid_items}]}\n"; |
|
289
|
|
|
|
|
|
|
# print "<$tag> => <@i>\n"; |
|
290
|
|
|
|
|
|
|
|
|
291
|
5580
|
100
|
|
|
|
8122
|
unless (@i) { |
|
292
|
|
|
|
|
|
|
# unless $tag eq "CONT" || $tag eq "CONC" |
|
293
|
|
|
|
|
|
|
# || substr($tag, 0, 1) eq "_"; |
|
294
|
|
|
|
|
|
|
# TODO - should CONT and CONC be allowed anywhere? |
|
295
|
5
|
50
|
|
|
|
26
|
unless (substr($tag, 0, 1) eq "_") { |
|
296
|
|
|
|
|
|
|
warn "$self->{file}:$r->{line}: $tag is not a sub-item of $t\n", |
|
297
|
|
|
|
|
|
|
"Valid sub-items are ", |
|
298
|
0
|
0
|
|
|
|
0
|
join(", ", sort keys %{$grammar->{_valid_items}}), "\n" |
|
|
0
|
|
|
|
|
0
|
|
|
299
|
|
|
|
|
|
|
unless $test; |
|
300
|
0
|
|
|
|
|
0
|
$match = 0; |
|
301
|
0
|
|
|
|
|
0
|
next; |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# print "$self->{file}:$r->{line}: Ambiguous tag $tag as sub-item of $t, ", |
|
306
|
|
|
|
|
|
|
# "found ", scalar @i, " matches\n" if @i > 1; |
|
307
|
5580
|
|
|
|
|
5815
|
my $m = 0; |
|
308
|
5580
|
|
|
|
|
6186
|
for my $i (@i) { |
|
309
|
5575
|
50
|
|
|
|
8076
|
last if $m = $self->parse($r, $i, @i > 1); |
|
310
|
|
|
|
|
|
|
} |
|
311
|
|
|
|
|
|
|
|
|
312
|
5580
|
50
|
33
|
|
|
10362
|
if (@i > 1 && !$m) { |
|
313
|
|
|
|
|
|
|
# TODO - I'm not even sure if this can happen. |
|
314
|
0
|
|
|
|
|
0
|
warn "$self->{file}:$r->{line}:" , |
|
315
|
|
|
|
|
|
|
"Ambiguous tag $tag as sub-item of $t, ", |
|
316
|
|
|
|
|
|
|
"found ", scalar @i, " matches, all of which have errors. ", |
|
317
|
|
|
|
|
|
|
"Reporting errors from last match.\n"; |
|
318
|
0
|
|
|
|
|
0
|
$self->parse($r, $i[-1]); |
|
319
|
0
|
|
|
|
|
0
|
$match = 0; |
|
320
|
|
|
|
|
|
|
# TODO - count the errors in each match and use the best. |
|
321
|
|
|
|
|
|
|
} |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
# print "parsed $match\n"; |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
$match |
|
326
|
6531
|
|
|
|
|
11518
|
} |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
sub collect_xrefs { |
|
329
|
21861
|
|
|
21861
|
1
|
20917
|
my $self = shift; |
|
330
|
21861
|
|
|
|
|
22630
|
my ($callback) = @_; |
|
331
|
21861
|
100
|
|
|
|
34266
|
$self->{gedcom}{xrefs}{$self->{xref}} = $self if defined $self->{xref}; |
|
332
|
21861
|
|
|
|
|
20214
|
$_->collect_xrefs($callback) for @{$self->{items}}; |
|
|
21861
|
|
|
|
|
35778
|
|
|
333
|
21861
|
|
|
|
|
32366
|
$self |
|
334
|
|
|
|
|
|
|
} |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub resolve_xref { |
|
337
|
5287
|
|
|
5287
|
1
|
9444
|
shift->{gedcom}->resolve_xref(@_); |
|
338
|
|
|
|
|
|
|
} |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
sub resolve { |
|
341
|
15632
|
|
|
15632
|
1
|
19560
|
my $self = shift; |
|
342
|
|
|
|
|
|
|
my @x = map { |
|
343
|
15632
|
|
|
|
|
18251
|
ref($_) |
|
344
|
|
|
|
|
|
|
? $_ |
|
345
|
14083
|
100
|
|
|
|
25297
|
: do { my $x = $self->{gedcom}->resolve_xref($_); defined $x ? $x : () } |
|
|
1387
|
100
|
|
|
|
2498
|
|
|
|
1387
|
|
|
|
|
2569
|
|
|
346
|
|
|
|
|
|
|
} @_; |
|
347
|
15632
|
100
|
|
|
|
30620
|
wantarray ? @x : $x[0] |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub resolve_xrefs { |
|
351
|
22803
|
|
|
22803
|
1
|
24511
|
my $self = shift; |
|
352
|
22803
|
|
|
|
|
26734
|
my ($callback) = @_; |
|
353
|
22803
|
100
|
|
|
|
45285
|
if (my $xref = $self->{gedcom}->resolve_xref($self->{value})) { |
|
354
|
2572
|
|
|
|
|
3014
|
$self->{value} = $xref; |
|
355
|
|
|
|
|
|
|
} |
|
356
|
22803
|
|
|
|
|
25041
|
$_->resolve_xrefs($callback) for @{$self->_items}; |
|
|
22803
|
|
|
|
|
38941
|
|
|
357
|
22803
|
|
|
|
|
42237
|
$self |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub unresolve_xrefs { |
|
361
|
15156
|
|
|
15156
|
1
|
15024
|
my $self = shift;; |
|
362
|
15156
|
|
|
|
|
16305
|
my ($callback) = @_; |
|
363
|
|
|
|
|
|
|
$self->{value} = $self->{value}{xref} |
|
364
|
|
|
|
|
|
|
if defined $self->{value} |
|
365
|
|
|
|
|
|
|
and UNIVERSAL::isa $self->{value}, "Gedcom::Record" |
|
366
|
15156
|
50
|
100
|
|
|
49910
|
and exists $self->{value}{xref}; |
|
|
|
|
66
|
|
|
|
|
|
367
|
15156
|
|
|
|
|
14502
|
$_->unresolve_xrefs($callback) for @{$self->_items}; |
|
|
15156
|
|
|
|
|
19435
|
|
|
368
|
15156
|
|
|
|
|
22935
|
$self |
|
369
|
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
my $D = 0; # turn on debug output |
|
372
|
|
|
|
|
|
|
my $I = -1; # indent for debug output |
|
373
|
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
sub validate_syntax { |
|
375
|
69989
|
|
|
69989
|
0
|
71866
|
my $self = shift; |
|
376
|
69989
|
100
|
|
|
|
110122
|
return 1 unless exists $self->{grammar}; |
|
377
|
53489
|
|
|
|
|
51076
|
my $ok = 1; |
|
378
|
|
|
|
|
|
|
$self->{gedcom}{validate_callback}->($self) |
|
379
|
53489
|
50
|
|
|
|
103910
|
if defined $self->{gedcom}{validate_callback}; |
|
380
|
53489
|
|
|
|
|
61943
|
my $grammar = $self->{grammar}; |
|
381
|
53489
|
|
|
|
|
49103
|
$I++; |
|
382
|
|
|
|
|
|
|
print " " x $I . "validate_syntax(" . |
|
383
|
53489
|
0
|
|
|
|
63007
|
(defined $grammar->{tag} ? $grammar->{tag} : "") . ")\n" if $D; |
|
|
|
50
|
|
|
|
|
|
|
384
|
53489
|
|
|
|
|
65581
|
my $file = $self->{gedcom}{record}{file}; |
|
385
|
|
|
|
|
|
|
my $here = "$file:$self->{line}: $self->{tag}" . |
|
386
|
53489
|
100
|
|
|
|
115732
|
(defined $self->{xref} ? " $self->{xref}" : ""); |
|
387
|
|
|
|
|
|
|
# print "$self->{line}: "; $self->print; |
|
388
|
|
|
|
|
|
|
$ok = 0, warn "$here: $self->{tag} Can't contain a value ($self->{value})\n" |
|
389
|
|
|
|
|
|
|
if defined $self->{value} && length $self->{value} && |
|
390
|
53489
|
100
|
100
|
|
|
176024
|
!defined $grammar->{value}; |
|
|
|
|
100
|
|
|
|
|
|
391
|
53489
|
|
|
|
|
56555
|
my %counts; |
|
392
|
53489
|
|
|
|
|
50498
|
for my $record (@{$self->_items}) { |
|
|
53489
|
|
|
|
|
82184
|
|
|
393
|
69928
|
50
|
|
|
|
89333
|
print " " x $I . "level $record->{level} on $self->{level}\n" if $D; |
|
394
|
|
|
|
|
|
|
$ok = 0, |
|
395
|
|
|
|
|
|
|
warn "$here: Can't add level $record->{level} to $self->{level}\n" |
|
396
|
69928
|
50
|
|
|
|
126770
|
if $record->{level} > $self->{level} + 1; |
|
397
|
69928
|
|
|
|
|
113814
|
$counts{$record->{tag}}++; |
|
398
|
69928
|
100
|
|
|
|
94643
|
$ok = 0 unless $record->validate_syntax; |
|
399
|
|
|
|
|
|
|
} |
|
400
|
53489
|
|
|
|
|
86972
|
my $valid_items = $grammar->valid_items; |
|
401
|
53489
|
|
|
|
|
212507
|
for my $tag (sort keys %$valid_items) { |
|
402
|
563458
|
|
|
|
|
544313
|
for my $g (@{$valid_items->{$tag}}) { |
|
|
563458
|
|
|
|
|
702825
|
|
|
403
|
637008
|
|
|
|
|
653679
|
my $min = $g->{min}; |
|
404
|
637008
|
|
|
|
|
639197
|
my $max = $g->{max}; |
|
405
|
637008
|
|
100
|
|
|
1130659
|
my $matches = delete $counts{$tag} || 0; |
|
406
|
637008
|
100
|
|
|
|
1076357
|
my $msg = "$here has $matches $tag" . ($matches == 1 ? "" : "s"); |
|
407
|
637008
|
50
|
|
|
|
821449
|
print " " x $I . "$msg - min is $min max is $max\n" if $D; |
|
408
|
637008
|
50
|
|
|
|
777558
|
$ok = 0, warn "$msg - minimum is $min\n" if $matches < $min; |
|
409
|
637008
|
100
|
100
|
|
|
1102814
|
$ok = 0, warn "$msg - maximum is $max\n" if $matches > $max && $max; |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
} |
|
412
|
53489
|
|
|
|
|
81106
|
for my $tag (keys %counts) { |
|
413
|
60
|
|
|
|
|
335
|
for my $c ($self->tag_record($tag)) { |
|
414
|
60
|
50
|
|
|
|
261
|
$ok = 0, |
|
415
|
|
|
|
|
|
|
warn "$file:$c->{line}: $tag is not a sub-item of $self->{tag}\n" |
|
416
|
|
|
|
|
|
|
unless substr($tag, 0, 1) eq "_"; |
|
417
|
|
|
|
|
|
|
# unless $tag eq "CONT" || $tag eq "CONC" || substr($tag, 0, 1) eq "_"; |
|
418
|
|
|
|
|
|
|
# TODO - should CONT and CONC be allowed anywhere? |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
} |
|
421
|
53489
|
|
|
|
|
50773
|
$I--; |
|
422
|
53489
|
|
|
|
|
106439
|
$ok; |
|
423
|
|
|
|
|
|
|
} |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
my $Check = { |
|
426
|
|
|
|
|
|
|
INDI => { |
|
427
|
|
|
|
|
|
|
FAMS => [ "HUSB", "WIFE" ], |
|
428
|
|
|
|
|
|
|
FAMC => [ "CHIL" ] |
|
429
|
|
|
|
|
|
|
}, |
|
430
|
|
|
|
|
|
|
FAM => { |
|
431
|
|
|
|
|
|
|
HUSB => [ "FAMS" ], |
|
432
|
|
|
|
|
|
|
WIFE => [ "FAMS" ], |
|
433
|
|
|
|
|
|
|
CHIL => [ "FAMC" ], |
|
434
|
|
|
|
|
|
|
}, |
|
435
|
|
|
|
|
|
|
}; |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
sub validate_semantics { |
|
438
|
8770
|
|
|
8770
|
1
|
9317
|
my $self = shift; |
|
439
|
8770
|
100
|
100
|
|
|
21112
|
return 1 unless $self->{tag} eq "INDI" || $self->{tag} eq "FAM"; |
|
440
|
|
|
|
|
|
|
# print "validating: "; $self->print; print $self->summary, "\n"; |
|
441
|
8405
|
|
|
|
|
8384
|
my $ok = 1; |
|
442
|
8405
|
|
|
|
|
10818
|
my $xrefs = $self->{gedcom}{xrefs}; |
|
443
|
8405
|
|
|
|
|
11645
|
my $chk = $Check->{$self->{tag}}; |
|
444
|
8405
|
|
|
|
|
14148
|
for my $f (keys %$chk) { |
|
445
|
19630
|
|
|
|
|
21678
|
my $found = 1; |
|
446
|
|
|
|
|
|
|
RECORD: |
|
447
|
19630
|
|
|
|
|
27126
|
for my $record ($self->tag_value($f)) { |
|
448
|
15244
|
|
|
|
|
16736
|
$found = 0; |
|
449
|
15244
|
100
|
|
|
|
24579
|
$record = $xrefs->{$record} unless ref $record; |
|
450
|
15244
|
100
|
|
|
|
21886
|
if ($record) { |
|
451
|
15240
|
|
|
|
|
15270
|
for my $back (@{$chk->{$f}}) { |
|
|
15240
|
|
|
|
|
21303
|
|
|
452
|
|
|
|
|
|
|
# print "back $back\n"; |
|
453
|
17160
|
|
|
|
|
23795
|
for my $i ($record->tag_value($back)) { |
|
454
|
|
|
|
|
|
|
# print "record is $i\n"; |
|
455
|
20400
|
100
|
|
|
|
33909
|
$i = $xrefs->{$i} unless ref $i; |
|
456
|
20400
|
100
|
66
|
|
|
51264
|
if ($i && $i->{xref} eq $self->{xref}) { |
|
457
|
15240
|
|
|
|
|
15626
|
$found = 1; |
|
458
|
|
|
|
|
|
|
# print "found...\n"; |
|
459
|
15240
|
|
|
|
|
24071
|
next RECORD; |
|
460
|
|
|
|
|
|
|
} |
|
461
|
|
|
|
|
|
|
} |
|
462
|
|
|
|
|
|
|
} |
|
463
|
0
|
0
|
|
|
|
0
|
unless ($found) { |
|
464
|
|
|
|
|
|
|
# TODO - use the line of the offending record |
|
465
|
0
|
|
|
|
|
0
|
$ok = 0; |
|
466
|
0
|
|
|
|
|
0
|
my $file = $self->{gedcom}{record}{file}; |
|
467
|
|
|
|
|
|
|
warn "$file:$self->{line}: $f $record->{xref} " . |
|
468
|
|
|
|
|
|
|
"does not reference $self->{tag} $self->{xref}. " . |
|
469
|
|
|
|
|
|
|
"Add the line:\n" . |
|
470
|
|
|
|
|
|
|
"$file:" . ($record->{line} + 1) . ": 1 " . |
|
471
|
0
|
|
|
|
|
0
|
join("or ", @{$chk->{$f}}) . " $self->{xref}\n"; |
|
|
0
|
|
|
|
|
0
|
|
|
472
|
|
|
|
|
|
|
} |
|
473
|
|
|
|
|
|
|
} |
|
474
|
|
|
|
|
|
|
} |
|
475
|
|
|
|
|
|
|
} |
|
476
|
8405
|
|
|
|
|
19587
|
$ok; |
|
477
|
|
|
|
|
|
|
} |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
sub normalise_dates { |
|
480
|
7578
|
|
|
7578
|
1
|
8877
|
my $self = shift; |
|
481
|
7578
|
50
|
|
|
|
10680
|
unless ($INC{"Date/Manip.pm"}) { |
|
482
|
0
|
|
|
|
|
0
|
warn "Date::Manip.pm is required to use normalise_dates()"; |
|
483
|
0
|
|
|
|
|
0
|
return; |
|
484
|
|
|
|
|
|
|
} |
|
485
|
7578
|
50
|
33
|
|
|
18340
|
if( eval { Date::Manip->VERSION( 6 ) } && |
|
|
7578
|
|
|
|
|
45993
|
|
|
486
|
7578
|
|
|
|
|
54286
|
!eval { Date::Manip->VERSION( 6.13 ) } ) { |
|
487
|
0
|
|
|
|
|
0
|
warn "Unable to normalize dates with this version of Date::Manip. " . |
|
488
|
|
|
|
|
|
|
"Please upgrade to version 6.13."; |
|
489
|
|
|
|
|
|
|
return |
|
490
|
0
|
|
|
|
|
0
|
} |
|
491
|
7578
|
|
100
|
|
|
14136
|
my $format = shift || "%A, %E %B %Y"; |
|
492
|
7578
|
100
|
66
|
|
|
25036
|
if (defined $self->{tag} && $self->{tag} =~ /^date$/i) { |
|
493
|
906
|
50
|
33
|
|
|
3712
|
if (defined $self->{value} && $self->{value}) { |
|
494
|
|
|
|
|
|
|
# print "date was $self->{value}\n"; |
|
495
|
906
|
|
|
|
|
2249
|
my @dates = split / or /, $self->{value}; |
|
496
|
906
|
|
|
|
|
1346
|
for my $dt (@dates) { |
|
497
|
|
|
|
|
|
|
# Don't change the date if it looks like 'AFT 1989'. |
|
498
|
|
|
|
|
|
|
# AFT means AFTER and ParseDate returns the current date and the tests |
|
499
|
|
|
|
|
|
|
# are failing. |
|
500
|
|
|
|
|
|
|
# Current date can symbolize such an "after" date, but can also |
|
501
|
|
|
|
|
|
|
# symbolize a very specific point in time and that could also confuse |
|
502
|
|
|
|
|
|
|
# the user. |
|
503
|
906
|
100
|
|
|
|
1656
|
next if $dt =~ /^AFT/; |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
# Don't change the date if it is just < 7 digits. |
|
506
|
900
|
100
|
66
|
|
|
3242
|
if ($dt !~ /^\s*(\d+)\s*$/ || length $1 > 6) { |
|
507
|
642
|
|
|
|
|
1516
|
my $date = ParseDate($dt); |
|
508
|
642
|
|
|
|
|
947602
|
my $d = UnixDate($date, $format); |
|
509
|
642
|
100
|
|
|
|
340920
|
$dt = $d if $d; |
|
510
|
|
|
|
|
|
|
} |
|
511
|
|
|
|
|
|
|
} |
|
512
|
906
|
|
|
|
|
2133
|
$self->{value} = join " or ", @dates; |
|
513
|
|
|
|
|
|
|
# print "date is $self->{value}\n"; |
|
514
|
|
|
|
|
|
|
} |
|
515
|
|
|
|
|
|
|
} |
|
516
|
7578
|
|
|
|
|
8423
|
$_->normalise_dates($format) for @{$self->_items}; |
|
|
7578
|
|
|
|
|
12666
|
|
|
517
|
7578
|
100
|
|
|
|
12839
|
$self->delete_items if $self->level > 1; |
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
sub renumber { |
|
521
|
13600
|
|
|
13600
|
1
|
13840
|
my $self = shift; |
|
522
|
13600
|
|
|
|
|
14701
|
my ($args, $recurse) = @_; |
|
523
|
|
|
|
|
|
|
# TODO - add the xref if there is supposed to be one |
|
524
|
13600
|
100
|
100
|
|
|
31585
|
return if exists $self->{recursed} or not defined $self->{xref}; |
|
525
|
|
|
|
|
|
|
# we can't actually change the xrefs until the end |
|
526
|
4496
|
100
|
|
|
|
8169
|
my $x = $self->{tag} eq "SUBM" ? "SUBM" : substr $self->{tag}, 0, 1; |
|
527
|
|
|
|
|
|
|
$self->{new_xref} = $x . ++$args->{$self->{tag}} |
|
528
|
4496
|
100
|
|
|
|
8222
|
unless exists $self->{new_xref}; |
|
529
|
4496
|
100
|
66
|
|
|
10007
|
return unless $recurse and not exists $self->{recursed}; |
|
530
|
1736
|
|
|
|
|
2247
|
$self->{recursed} = 1; |
|
531
|
1736
|
100
|
|
|
|
4444
|
if ($self->{tag} eq "INDI") { |
|
532
|
1121
|
|
|
|
|
1470
|
my @r = map { $self->$_() } |
|
|
6726
|
|
|
|
|
14161
|
|
|
533
|
|
|
|
|
|
|
qw( fams famc spouse children parents siblings ); |
|
534
|
1121
|
|
|
|
|
2569
|
$_->renumber($args, 0) for @r; |
|
535
|
1121
|
|
|
|
|
2043
|
$_->renumber($args, 1) for @r; |
|
536
|
|
|
|
|
|
|
} |
|
537
|
|
|
|
|
|
|
} |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
sub child_value { |
|
540
|
|
|
|
|
|
|
# NOTE - This function is deprecated - use tag_value instead |
|
541
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
542
|
0
|
|
|
|
|
|
$self->tag_value(@_) |
|
543
|
|
|
|
|
|
|
} |
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
sub child_values { |
|
546
|
|
|
|
|
|
|
# NOTE - This function is deprecated - use tag_value instead |
|
547
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
548
|
0
|
|
|
|
|
|
$self->tag_value(@_) |
|
549
|
|
|
|
|
|
|
} |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
sub compare { |
|
552
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
553
|
0
|
|
|
|
|
|
my ($r) = @_; |
|
554
|
0
|
|
|
|
|
|
Gedcom::Comparison->new($self, $r) |
|
555
|
|
|
|
|
|
|
} |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
sub summary { |
|
558
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
559
|
0
|
|
|
|
|
|
my $s = ""; |
|
560
|
0
|
|
|
|
|
|
$s .= sprintf "%-5s", $self->{xref}; |
|
561
|
0
|
|
|
|
|
|
my $r = $self->tag_record("NAME"); |
|
562
|
0
|
0
|
|
|
|
|
$s .= sprintf " %-40s", $r ? $r->{value} : ""; |
|
563
|
0
|
|
|
|
|
|
$r = $self->tag_record("SEX"); |
|
564
|
0
|
0
|
|
|
|
|
$s .= sprintf " %1s", $r ? $r->{value} : ""; |
|
565
|
0
|
|
|
|
|
|
my $d = ""; |
|
566
|
0
|
0
|
0
|
|
|
|
if ($r = $self->tag_record("BIRT") and my $date = $r->tag_record("DATE")) { |
|
567
|
0
|
|
|
|
|
|
$d = $date->{value}; |
|
568
|
|
|
|
|
|
|
} |
|
569
|
0
|
|
|
|
|
|
$s .= sprintf " %16s", $d; |
|
570
|
0
|
|
|
|
|
|
$s; |
|
571
|
|
|
|
|
|
|
} |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
1; |
|
574
|
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
__END__ |