1 /*
2 Copyright (c) 1991-1999 Thomas T. Wetmore IV
3
4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation
6 files (the "Software"), to deal in the Software without
7 restriction, including without limitation the rights to use, copy,
8 modify, merge, publish, distribute, sublicense, and/or sell copies
9 of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 */
24 /* modified 05 Jan 2000 by Paul B. McBride (pmcbride@tiac.net) */
25 /* modified 2000-08-22 J.F.Chandler */
26 /*=============================================================
27 * valgdcom.c -- Validate GEDCOM file
28 * Copyright(c) 1993-96 by T.T. Wetmore IV; all rights reserved
29 * pre-SourceForge version information:
30 * 2.3.4 - 24 Jun 93 2.3.5 - 02 Sep 93
31 * 3.0.0 - 06 Jul 94 3.0.2 - 18 Feb 95
32 * 3.0.3 - 29 Jun 96
33 *===========================================================*/
34
35 #include "sys_inc.h"
36 #include "llstdlib.h"
37 /* llstdlib.h pulls in standard.h, config.h, sys_inc.h */
38 #include "table.h"
39 #include "translat.h"
40 #include "gedcom.h"
41 #include "gedcheck.h"
42 #include "liflines.h"
43 #include "llinesi.h"
44 #include "impfeed.h"
45 #include "lloptions.h"
46 #include "zstr.h"
47 #include "codesets.h"
48 #include "btree.h"
49
50 #ifndef INCLUDED_STDARG_H
51 #include <stdarg.h>
52 #define INCLUDED_STDARG_H
53 #endif
54
55 extern STRING qSmisixr, qSmisfxr;
56 extern STRING qSmulper, qSmulfam;
57 extern STRING qSmatper, qSmatfam;
58 extern STRING qSundper, qSundfam, qSundsrc;
59 extern STRING qSundevn, qSbadlev, qSnoname;
60
61 /* external data set by check_stdkeys() , used by addmissingkeys() */
62
63 INT gd_itot = 0; /* total number of individuals */
64 INT gd_ftot = 0; /* total number of families */
65 INT gd_stot = 0; /* total number of sources */
66 INT gd_etot = 0; /* total number of events */
67 INT gd_xtot = 0; /* total number of others */
68 INT gd_imax = 0; /* maximum individual key number */
69 INT gd_fmax = 0; /* maximum family key number */
70 INT gd_smax = 0; /* maximum source key number */
71 INT gd_emax = 0; /* maximum event key number */
72 INT gd_xmax = 0; /* maximum other key number */
73
74 static TABLE convtab = NULL;
75 static INT rec_type;
76 static BOOLEAN named = FALSE; /* found a NAME in current INDI ? */
77 static INT members = 0; /* number members (HUSB,WIFE,CHIL) in current FAM */
78 static INT person = -1;
79 static INT family = -1;
80 static INT event = -1;
81 static INT source = -1;
82 static INT other = -1;
83 static INT struct_len = 0;
84 static INT struct_max = 0;
85 static INT num_errors;
86 static INT num_warns;
87 static INT defline;
88 static BOOLEAN f_logopen = FALSE;
89 static FILE *f_flog = 0;
90 static char f_logpath[MAXPATHLEN] = "import.log";
91
92 static STRING qSundrec = N_("Record %s is referred to but not defined.");
93 static STRING qSundrecl = N_("Line %d: Reference to undefined record %s");
94 static STRING qSlinlev1 = N_("Line %d: Tag %s found in unexpected record: %s %s.");
95
96 ELMNT *index_data = NULL;
97
98
99 /*********************************************
100 * local function prototypes
101 *********************************************/
102
103 /* alphabetical */
104 static INT add_even_defn(IMPORT_FEEDBACK ifeed, STRING, INT);
105 static INT add_fam_defn(IMPORT_FEEDBACK ifeed, STRING, INT);
106 static INT add_indi_defn(IMPORT_FEEDBACK ifeed, STRING, INT, ELMNT*);
107 static INT add_othr_defn(IMPORT_FEEDBACK ifeed, STRING, INT);
108 static INT add_sour_defn(IMPORT_FEEDBACK ifeed, STRING, INT);
109 static INT add_to_structures(STRING, ELMNT);
110 static void append_path(ZSTR zstr, char delim, CNSTRING str);
111 static int check_akey (int firstchar, STRING keyp, INT *maxp);
112 static void check_even_links(IMPORT_FEEDBACK ifeed, ELMNT);
113 static void check_fam_links(IMPORT_FEEDBACK ifeed, ELMNT);
114 static void check_indi_links(IMPORT_FEEDBACK ifeed, ELMNT per);
115 static void check_level1_tag(IMPORT_FEEDBACK ifeed, CNSTRING tag, CNSTRING val, INT line, CNSTRING tag0, CNSTRING xref0);
116 static void check_othr_links(IMPORT_FEEDBACK ifeed, ELMNT);
117 static void check_references(IMPORT_FEEDBACK ifeed);
118 static void check_sour_links(IMPORT_FEEDBACK ifeed, ELMNT src);
119 static void clear_structures(void);
120 static ELMNT create_elmnt(CHAR eltype, CNSTRING xref);
121 static void free_elmnt(ELMNT el);
122 static void handle_fam_lev1(IMPORT_FEEDBACK ifeed, STRING tag, STRING val, INT line, CNSTRING tag0, CNSTRING xref0);
123 static void handle_indi_lev1(IMPORT_FEEDBACK ifeed, STRING tag, STRING val, INT line, CNSTRING tag0, CNSTRING xref0);
124 static void handle_head_lev1(IMPORT_FEEDBACK ifeed, STRING, STRING, INT);
125 static void handle_trlr_lev1(IMPORT_FEEDBACK ifeed, STRING, STRING, INT);
126 static void handle_value(STRING, INT);
127 static BOOLEAN openlog(void);
128 static void handle_warn(IMPORT_FEEDBACK ifeed, STRING, ...);
129 static void handle_err(IMPORT_FEEDBACK ifeed, STRING, ...);
130 static void set_import_log(STRING logpath);
131 static void report_missing_value(IMPORT_FEEDBACK ifeed, STRING tag, INT line, CNSTRING tag0, CNSTRING xref0);
132
133 /*===================================================
134 * validate_gedcom -- Validate GEDCOM records in file
135 *=================================================*/
136 BOOLEAN
validate_gedcom(IMPORT_FEEDBACK ifeed,FILE * fp)137 validate_gedcom (IMPORT_FEEDBACK ifeed, FILE *fp)
138 {
139 INT lev, rc, curlev = 0;
140 INT nhead, ntrlr, nindi, nfam, nsour, neven, nothr;
141 ELMNT el;
142 XLAT xlat = transl_get_predefined_xlat(MGDIN);
143 STRING xref, tag, val, msg;
144 STRING tag0=0;
145 STRING xref0=0;
146
147 nhead = ntrlr = nindi = nfam = nsour = neven = nothr = 0;
148 num_errors = num_warns = 0;
149 f_logopen = FALSE;
150 f_flog = 0;
151 set_import_log(getlloptstr("ImportLog", "errs.log"));
152 defline = 0;
153 curlev = 0;
154 clear_structures();
155 convtab = create_table_int();
156
157
158 rc = file_to_line(fp, xlat, &lev, &xref, &tag, &val, &msg);
159 xref = xref ? rmvat(xref) : NULL;
160 rec_type = OTHR_REC;
161 while (rc != DONE) {
162 if (lev > curlev + 1 || rc == ERROR) {
163 if (rc == ERROR)
164 handle_err(ifeed, msg);
165 else
166 handle_err(ifeed, qSbadlev, flineno);
167 handle_value(val, flineno);
168 curlev = lev;
169 rc = file_to_line(fp, xlat, &lev, &xref, &tag, &val,
170 &msg);
171 xref = xref ? rmvat(xref) : NULL;
172 continue;
173 }
174 if (lev > 1) {
175 handle_value(val, flineno);
176 curlev = lev;
177 rc = file_to_line(fp, xlat, &lev, &xref, &tag, &val,
178 &msg);
179 xref = xref ? rmvat(xref) : NULL;
180 continue;
181 }
182 if (lev == 0) {
183 if (rec_type == INDI_REC) {
184 if (!named && getlloptint("RequireNames", 0)) {
185 handle_err(ifeed, qSnoname, defline);
186 }
187 }
188 if (rec_type == FAM_REC) {
189 if (!members) {
190 handle_warn(ifeed, _("Line %d: Family has no members (%s %s).")
191 , defline, tag0, xref0);
192 }
193 }
194 defline = flineno;
195 strupdate(&tag0, tag); /* store current level 0 tag */
196 strupdate(&xref0, xref); /* store current level 0 xref (eg I15) */
197 if (eqstr("HEAD", tag)) {
198 rec_type = (nhead==0 ? HEAD_REC : IGNR_REC);
199 } else if (eqstr("TRLR", tag)) {
200 rec_type = (ntrlr==0 ? TRLR_REC : IGNR_REC);
201 } else {
202 INT count=0;
203 if (eqstr("INDI", tag)) {
204 count = ++nindi;
205 rec_type = INDI_REC;
206 named = FALSE;
207 person = add_indi_defn(ifeed, xref, flineno, &el);
208 /* pretend a NAME if the record is skipped */
209 if (person == -2) named = TRUE;
210 } else if (eqstr("FAM", tag)) {
211 count = ++nfam;
212 rec_type = FAM_REC;
213 members = 0;
214 family = add_fam_defn(ifeed, xref, flineno);
215 } else if (eqstr("EVEN", tag)) {
216 count = ++neven;
217 rec_type = EVEN_REC;
218 event = add_even_defn(ifeed, xref, flineno);
219 } else if (eqstr("SOUR", tag)) {
220 count = ++nsour;
221 rec_type = SOUR_REC;
222 source = add_sour_defn(ifeed, xref, flineno);
223 } else {
224 count = ++nothr;
225 rec_type = OTHR_REC;
226 other = add_othr_defn(ifeed, xref, flineno);
227 }
228 if (ifeed && ifeed->validated_rec_fnc) {
229 char ctype = (rec_type==OTHR_REC) ? 'X': tag[0];
230 ifeed->validated_rec_fnc(ctype, tag, count);
231 }
232 }
233 } else {
234 /* specific handling for specific record types */
235 if (rec_type == HEAD_REC)
236 handle_head_lev1(ifeed, tag, val, flineno);
237 else if (rec_type == TRLR_REC)
238 handle_trlr_lev1(ifeed, tag, val, flineno);
239 else if (rec_type == INDI_REC)
240 handle_indi_lev1(ifeed, tag, val, flineno, tag0, xref0);
241 else if (rec_type == FAM_REC)
242 handle_fam_lev1(ifeed, tag, val, flineno, tag0, xref0);
243 else
244 handle_value(val, flineno);
245 /* specific handling for specific tag types */
246 check_level1_tag(ifeed, tag, val, flineno, tag0, xref0);
247 }
248 curlev = lev;
249 rc = file_to_line(fp, xlat, &lev, &xref, &tag, &val, &msg);
250 xref = xref ? rmvat(xref) : NULL;
251 }
252 if (rec_type == INDI_REC && !named)
253 handle_err(ifeed, qSnoname, defline);
254 check_references(ifeed);
255 if (f_logopen) {
256 fclose(f_flog);
257 f_logopen = FALSE;
258 f_flog = 0;
259 }
260 strfree(&tag0);
261 return num_errors == 0;
262 }
263 /*=======================================
264 * create_elmnt -- Return newly alloc'd ELMNT
265 *=====================================*/
266 static ELMNT
create_elmnt(CHAR eltype,CNSTRING xref)267 create_elmnt (CHAR eltype, CNSTRING xref)
268 {
269 ELMNT el = (ELMNT) stdalloc(sizeof(*el));
270 memset(el, 0, sizeof(*el));
271 Type(el) = eltype;
272 if (xref)
273 Key(el) = strsave(xref);
274 return el;
275 }
276 /*=======================================
277 * free_elmnt -- Free memory of element
278 *=====================================*/
279 static void
free_elmnt(ELMNT el)280 free_elmnt (ELMNT el)
281 {
282 strfree(&el->key);
283 strfree(&el->newstr);
284 stdfree(el);
285 }
286 /*=======================================
287 * add_indi_defn -- Add person definition
288 * return index of person structure or
289 * -1 if unexplained error or
290 * -2 if explained error
291 * xref: ref value
292 * line: line num
293 * pel:
294 *=====================================*/
295 static INT
add_indi_defn(IMPORT_FEEDBACK ifeed,STRING xref,INT line,ELMNT * pel)296 add_indi_defn (IMPORT_FEEDBACK ifeed, STRING xref, INT line, ELMNT *pel)
297 {
298 ELMNT el;
299 INT dex;
300
301 *pel = NULL;
302 if (!xref || *xref == 0) {
303 handle_err(ifeed, _(qSmisixr), line);
304 return -2;
305 }
306 if ((dex = xref_to_index(xref)) == -1) {
307 *pel = el = create_elmnt(INDI_REC, xref);
308 xref = Key(el); /* dup'd copy of xref */
309 Line(el) = 0;
310 New(el) = NULL;
311 Sex(el) = 0;
312 Male(el) = 0;
313 Fmle(el) = 0;
314 dex = add_to_structures(xref, el);
315 } else
316 *pel = el = index_data[dex];
317 if (KNOWNTYPE(el) && Type(el) != INDI_REC) {
318 handle_err(ifeed, qSmatper, line, xref);
319 return -2;
320 }
321 if (Type(el) == INDI_REC) {
322 if (Line(el) && line) {
323 handle_err(ifeed, qSmulper, Line(el), line, xref);
324 return -2;
325 }
326 if (line) Line(el) = line;
327 return dex;
328 }
329 Type(el) = INDI_REC;
330 Line(el) = line;
331 Sex(el) = 0;
332 Male(el) = 0;
333 Fmle(el) = 0;
334 return dex;
335 }
336 /*======================================
337 * add_fam_defn -- Add family definition
338 * xref: ref value
339 * line: line num
340 *====================================*/
341 static INT
add_fam_defn(IMPORT_FEEDBACK ifeed,STRING xref,INT line)342 add_fam_defn (IMPORT_FEEDBACK ifeed, STRING xref, INT line)
343 {
344 ELMNT el;
345 INT dex;
346 if (!xref || *xref == 0) {
347 handle_err(ifeed, qSmisfxr, line);
348 return -1;
349 }
350 if ((dex = xref_to_index(xref)) == -1) {
351 el = create_elmnt(FAM_REC, xref);
352 Line(el) = 0;
353 New(el) = NULL;
354 Male(el) = 0;
355 Fmle(el) = 0;
356 dex = add_to_structures(xref, el);
357 } else {
358 el = index_data[dex];
359 }
360 if (KNOWNTYPE(el) && Type(el) != FAM_REC) {
361 handle_err(ifeed, qSmatfam, line, xref);
362 return -1;
363 }
364 if (Type(el) == FAM_REC) {
365 if (Line(el) && line) {
366 handle_err(ifeed, qSmulfam, Line(el), line, xref);
367 return -1;
368 }
369 if (line) Line(el) = line;
370 return dex;
371 }
372 Type(el) = FAM_REC;
373 Line(el) = line;
374 Male(el) = 0;
375 Fmle(el) = 0;
376 return dex;
377 }
378 /*=======================================
379 * add_sour_defn -- Add source definition
380 * xref: ref value
381 * line: line num
382 *=====================================*/
383 static INT
add_sour_defn(IMPORT_FEEDBACK ifeed,STRING xref,INT line)384 add_sour_defn (IMPORT_FEEDBACK ifeed, STRING xref, INT line)
385 {
386 ELMNT el;
387 INT dex;
388 if (!xref || *xref == 0) {
389 handle_err(ifeed
390 , _("Line %d: The source defined here has no key.")
391 , line);
392 return -1;
393 }
394 if ((dex = xref_to_index(xref)) == -1) {
395 el = create_elmnt(SOUR_REC, xref);
396 xref = Key(el); /* dup'd copy of xref */
397 Line(el) = 0;
398 New(el) = NULL;
399 dex = add_to_structures(xref, el);
400 } else
401 el = index_data[dex];
402 if (KNOWNTYPE(el) && Type(el) != SOUR_REC) {
403 handle_err(ifeed
404 , _("Line %d: Source %s has an incorrect key.")
405 , line, xref);
406 return -1;
407 }
408 if (Type(el) == SOUR_REC) {
409 if (Line(el) && line) {
410 handle_err(ifeed
411 , _("Lines %d and %d: Source %s is multiply defined.")
412 , Line(el), line, xref);
413 return -1;
414 }
415 if (line) Line(el) = line;
416 return dex;
417 }
418 Type(el) = SOUR_REC;
419 Line(el) = line;
420 return dex;
421 }
422 /*======================================
423 * add_even_defn -- Add event definition
424 * xref: ref value
425 * line: line num
426 *====================================*/
427 static INT
add_even_defn(IMPORT_FEEDBACK ifeed,STRING xref,INT line)428 add_even_defn (IMPORT_FEEDBACK ifeed, STRING xref, INT line)
429 {
430 ELMNT el;
431 INT dex;
432 if (!xref || *xref == 0) {
433 handle_err(ifeed
434 , _("Line %d: The event defined here has no key.")
435 , line);
436 return -1;
437 }
438 if ((dex = xref_to_index(xref)) == -1) {
439 el = create_elmnt(EVEN_REC, xref);
440 Line(el) = 0;
441 New(el) = NULL;
442 dex = add_to_structures(xref, el);
443 } else
444 el = index_data[dex];
445 if (KNOWNTYPE(el) && Type(el) != EVEN_REC) {
446 handle_err(ifeed
447 , _("Line %d: Event %s has an incorrect key.")
448 , line, xref);
449 return -1;
450 }
451 if (Type(el) == EVEN_REC) {
452 if (Line(el) && line) {
453 handle_err(ifeed
454 , _("Lines %d and %d: Event %s is multiply defined.")
455 , Line(el), line, xref);
456 return -1;
457 }
458 if (line) Line(el) = line;
459 return dex;
460 }
461 Type(el) = EVEN_REC;
462 Line(el) = line;
463 return dex;
464 }
465 /*==================================================
466 * add_othr_defn -- Add other record type definition
467 * xref: ref value
468 * line: line num
469 *================================================*/
470 static INT
add_othr_defn(IMPORT_FEEDBACK ifeed,STRING xref,INT line)471 add_othr_defn (IMPORT_FEEDBACK ifeed, STRING xref, INT line)
472 {
473 ELMNT el;
474 INT dex;
475 if (!xref || *xref == 0) {
476 handle_err(ifeed
477 , _("Line %d: The record defined here has no key.")
478 , line);
479 return -1;
480 }
481 if ((dex = xref_to_index(xref)) == -1) {
482 el = create_elmnt(OTHR_REC, xref);
483 Line(el) = 0;
484 New(el) = NULL;
485 dex = add_to_structures(xref, el);
486 } else
487 el = index_data[dex];
488 if (KNOWNTYPE(el) && Type(el) != OTHR_REC) {
489 handle_err(ifeed
490 , _("Line %d: Record %s has an incorrect key.")
491 , line, xref);
492 return -1;
493 }
494 if (Type(el) == OTHR_REC) {
495 if (Line(el) && line) {
496 handle_err(ifeed
497 , _("Lines %d and %d: Record %s is multiply defined.")
498 , Line(el), line, xref);
499 return -1;
500 }
501 if (line) Line(el) = line;
502 return dex;
503 }
504 Type(el) = OTHR_REC;
505 Line(el) = line;
506 return dex;
507 }
508 /*===========================================================
509 * handle_head_lev1 -- Handle level 1 lines in HEAD record
510 * Created: 2002-12-15 (Perry Rapp)
511 *=========================================================*/
512 static void
handle_head_lev1(IMPORT_FEEDBACK ifeed,STRING tag,STRING val,INT line)513 handle_head_lev1 (IMPORT_FEEDBACK ifeed, STRING tag, STRING val, INT line)
514 {
515 ifeed=ifeed; /* unused */
516 line=line; /* unused */
517 if (eqstr(tag, "CHAR")) {
518 strupdate(&gedcom_codeset_in, (val ? val : ""));
519 }
520 }
521 /*===========================================================
522 * handle_trlr_lev1 -- Handle level 1 lines in TRLR record
523 * Created: 2002-12-15 (Perry Rapp)
524 *=========================================================*/
525 static void
handle_trlr_lev1(IMPORT_FEEDBACK ifeed,STRING tag,STRING val,INT line)526 handle_trlr_lev1 (IMPORT_FEEDBACK ifeed, STRING tag, STRING val, INT line)
527 {
528 ifeed=ifeed; /* unused */
529 tag=tag; /* unused */
530 val=val; /* unused */
531 line=line; /* unused */
532 }
533 /*===========================================================
534 * report_missing_value -- Report line with incorrectly empty value
535 *=========================================================*/
536 static void
report_missing_value(IMPORT_FEEDBACK ifeed,STRING tag,INT line,CNSTRING tag0,CNSTRING xref0)537 report_missing_value (IMPORT_FEEDBACK ifeed, STRING tag, INT line, CNSTRING tag0, CNSTRING xref0)
538 {
539 handle_err(ifeed, _("Line %d: This %s line is missing a value field (%s %s).")
540 , line, tag, tag0, xref0);
541 }
542 /*===========================================================
543 * handle_indi_lev1 -- Handle level 1 lines in person records
544 *=========================================================*/
545 static void
handle_indi_lev1(IMPORT_FEEDBACK ifeed,STRING tag,STRING val,INT line,CNSTRING tag0,CNSTRING xref0)546 handle_indi_lev1 (IMPORT_FEEDBACK ifeed, STRING tag, STRING val, INT line, CNSTRING tag0, CNSTRING xref0)
547 {
548 ELMNT indi, pers;
549 ASSERT(person != -1);
550 if (person == -2) {
551 return;
552 }
553 indi = index_data[person];
554 if (eqstr(tag, "FAMC")) {
555 if (!pointer_value(val)) {
556 report_missing_value(ifeed, tag, line, tag0, xref0);
557 return;
558 }
559 (void) add_fam_defn(ifeed, rmvat(val), 0);
560 } else if (eqstr(tag, "FAMS")) {
561 if (!pointer_value(val)) {
562 report_missing_value(ifeed, tag, line, tag0, xref0);
563 return;
564 }
565 (void) add_fam_defn(ifeed, rmvat(val), 0);
566 } else if (eqstr(tag, "FATH")) {
567 if (!pointer_value(val)) {
568 report_missing_value(ifeed, tag, line, tag0, xref0);
569 return;
570 }
571 Male(indi) += 1;
572 if (add_indi_defn(ifeed, rmvat(val), 0, &pers) >= 0)
573 Sex(pers) |= BE_MALE;
574 } else if (eqstr(tag, "MOTH")) {
575 if (!pointer_value(val)) {
576 report_missing_value(ifeed, tag, line, tag0, xref0);
577 return;
578 }
579 Fmle(indi) += 1;
580 if (add_indi_defn(ifeed, rmvat(val), 0, &pers) >= 0)
581 Sex(pers) |= BE_FEMALE;
582 } else if (eqstr(tag, "SEX")) {
583 if (val && (*val == 'M'))
584 Sex(indi) |= IS_MALE;
585 else if (val && (*val == 'F'))
586 Sex(indi) |= IS_FEMALE;
587 } else if (eqstr(tag, "NAME")) {
588 named = TRUE;
589 if (!val || *val == 0 || !valid_name(val)) {
590 handle_err(ifeed, _("Line %d: Bad NAME syntax (%s %s)."),
591 line, tag0, xref0);
592 return;
593 }
594 } else
595 handle_value(val, line);
596 }
597 /*==========================================================
598 * handle_fam_lev1 -- Handle level 1 lines in family records
599 *========================================================*/
600 static void
handle_fam_lev1(IMPORT_FEEDBACK ifeed,STRING tag,STRING val,INT line,CNSTRING tag0,CNSTRING xref0)601 handle_fam_lev1 (IMPORT_FEEDBACK ifeed, STRING tag, STRING val, INT line, CNSTRING tag0, CNSTRING xref0)
602 {
603 ELMNT fam, pers;
604 fam = (family != -1) ? index_data[family] : NULL;
605 if (eqstr(tag, "HUSB")) {
606 ++members;
607 if (!pointer_value(val)) {
608 report_missing_value(ifeed, tag, line, tag0, xref0);
609 return;
610 }
611 if (fam) Male(fam) += 1;
612 if (add_indi_defn(ifeed, rmvat(val), 0, &pers) >= 0)
613 Sex(pers) |= BE_MALE;
614 } else if (eqstr(tag, "WIFE")) {
615 ++members;
616 if (!pointer_value(val)) {
617 report_missing_value(ifeed, tag, line, tag0, xref0);
618 return;
619 }
620 if (fam) Fmle(fam) += 1;
621 if (add_indi_defn(ifeed, rmvat(val), 0, &pers) >= 0)
622 Sex(pers) |= BE_FEMALE;
623 } else if (eqstr(tag, "CHIL")) {
624 ++members;
625 if (!pointer_value(val)) {
626 report_missing_value(ifeed, tag, line, tag0, xref0);
627 return;
628 }
629 (void) add_indi_defn(ifeed, rmvat(val), 0, &pers);
630 } else
631 handle_value(val, line);
632 }
633 /*==========================================================
634 * check_level1_tag -- Warnings for specific tags at level 1
635 *========================================================*/
636 static void
check_level1_tag(IMPORT_FEEDBACK ifeed,CNSTRING tag,CNSTRING val,INT line,CNSTRING tag0,CNSTRING xref0)637 check_level1_tag (IMPORT_FEEDBACK ifeed, CNSTRING tag, CNSTRING val, INT line, CNSTRING tag0, CNSTRING xref0)
638 {
639 /*
640 lifelines expects lineage-linking records (FAMS, FAMC, HUSB, & WIFE)
641 to be correct, so warn if any of them occur in unusual locations
642 */
643 val = val; /* unused */
644 if (eqstr(tag, "FAMS")) {
645 if (!eqstr(tag0, "INDI"))
646 handle_warn(ifeed, qSlinlev1, line, tag, tag0, xref0);
647 return;
648 }
649 if (eqstr(tag, "FAMC")) {
650 if (!eqstr(tag0, "INDI"))
651 handle_warn(ifeed, qSlinlev1, line, tag, tag0, xref0);
652 return;
653 }
654 if (eqstr(tag, "HUSB")) {
655 if (!eqstr(tag0, "FAM"))
656 handle_warn(ifeed, qSlinlev1, line, tag, tag0, xref0);
657 return;
658 }
659 if (eqstr(tag, "WIFE")) {
660 if (!eqstr(tag0, "FAM"))
661 handle_warn(ifeed, qSlinlev1, line, tag, tag0, xref0);
662 return;
663 }
664 }
665 /*=================================================
666 * check_akey -- Check for a standard format key
667 * lifelines assumes key's are of form
668 * a letter followed by a number with no leading zeros
669 * e.g. I248
670 *===============================================*/
671 static int
check_akey(int firstchar,STRING keyp,INT * maxp)672 check_akey (int firstchar,
673 STRING keyp,
674 INT *maxp)
675 {
676 INT val;
677 if(keyp && (*keyp == firstchar)) {
678 keyp++;
679 if(*keyp && (*keyp != '0') && isdigit(*keyp)) {
680 val = atoi(keyp);
681 if(val > *maxp) *maxp = val;
682 while(*keyp && isdigit((uchar)*keyp)) keyp++;
683 if(*keyp == '\0') return TRUE;
684 }
685 }
686 return FALSE;
687 }
688 /*=================================================
689 * check_stdkeys -- Check for standard format keys
690 *===============================================*/
691 int
check_stdkeys(void)692 check_stdkeys (void)
693 {
694 INT i;
695 int retval = TRUE;
696 gd_imax = 0; gd_itot = 0;
697 gd_fmax = 0; gd_ftot = 0;
698 gd_emax = 0; gd_etot = 0;
699 gd_smax = 0; gd_stot = 0;
700 gd_xmax = 0; gd_xtot = 0;
701 for (i = 0; retval && (i < struct_len); i++) {
702 ELMNT el = index_data[i];
703 STRING skey = Key(el);
704 /* check that key is not too long */
705 if (strlen(skey)>RKEYLEN) {
706 retval = FALSE;
707 break;
708 }
709 switch (Type(el)) {
710 case INDI_REC:
711 gd_itot++;
712 retval = check_akey('I', skey, &gd_imax);
713 break;
714 case FAM_REC:
715 gd_ftot++;
716 retval = check_akey('F', skey, &gd_fmax);
717 break;
718 case EVEN_REC:
719 gd_etot++;
720 retval = check_akey('E', skey, &gd_emax);
721 break;
722 case SOUR_REC:
723 gd_stot++;
724 retval = check_akey('S', skey, &gd_smax);
725 break;
726 case OTHR_REC:
727 gd_xtot++;
728 retval = check_akey('X', skey, &gd_xmax);
729 break;
730 default: retval = FALSE; break;
731 }
732 }
733 return retval;
734 }
735 /*================================================
736 * addmissingkeys -- add keys which are not in use
737 *==============================================*/
738 void
addmissingkeys(INT t)739 addmissingkeys (INT t) /* type of record: INDI_REC ... */
740 {
741 INT tmax, ttot;
742 INT i,j;
743 INT keystoadd;
744 char *kp;
745
746 switch(t)
747 {
748 case INDI_REC: ttot = gd_itot; tmax = gd_imax; break;
749 case FAM_REC: ttot = gd_ftot; tmax = gd_fmax; break;
750 case EVEN_REC: ttot = gd_etot; tmax = gd_emax; break;
751 case SOUR_REC: ttot = gd_stot; tmax = gd_smax; break;
752 case OTHR_REC: ttot = gd_xtot; tmax = gd_xmax; break;
753 default: return;
754 }
755
756 if((keystoadd = (tmax - ttot)) > 0) {
757 ASSERT(kp = (char *)stdalloc(tmax+1));
758 for(i = 0; i < tmax; i++) kp[i] = 0;
759 for (i = 0; i < struct_len; i++) {
760 ELMNT el = index_data[i];
761 if(Type(el) == t) {
762 j = atoi(Key(el)+1);
763 ASSERT(j>0);
764 ASSERT(j <= tmax);
765 kp[j] = 1;
766 }
767 }
768 /*
769 TO DO - ought to run this loop down instead of up
770 because it is much more efficient for xreffile.c
771 but I'm not sure if keystoadd is all of them
772 Also ought to tell xreffile.c to preallocate,
773 except I don't know how many of which.
774 Perry, 2001/01/05
775 */
776 for(i = 1; (keystoadd > 0) && (i <= tmax); i++) {
777 if(kp[i] == 0) {
778 switch(t)
779 {
780 case INDI_REC: addixref(i); break;
781 case FAM_REC: addfxref(i); break;
782 case EVEN_REC: addexref(i); break;
783 case SOUR_REC: addsxref(i); break;
784 case OTHR_REC: addxxref(i); break;
785 }
786 keystoadd--;
787 }
788 }
789 stdfree(kp);
790 }
791 }
792 /*=================================================
793 * check_references -- Check for undefined problems
794 *===============================================*/
795 static void
check_references(IMPORT_FEEDBACK ifeed)796 check_references (IMPORT_FEEDBACK ifeed)
797 {
798 INT i;
799 for (i = 0; i < struct_len; i++) {
800 ELMNT el = index_data[i];
801 switch (Type(el)) {
802 case INDI_REC: check_indi_links(ifeed, el); break;
803 case FAM_REC: check_fam_links(ifeed, el); break;
804 case EVEN_REC: check_even_links(ifeed, el); break;
805 case SOUR_REC: check_sour_links(ifeed, el); break;
806 case OTHR_REC: check_othr_links(ifeed, el); break;
807 default: handle_err(ifeed, qSundrecl, Line(el), Key(el));
808 }
809 }
810 }
811 /*=======================================
812 * check_indi_links -- Check person links
813 *=====================================*/
814 static void
check_indi_links(IMPORT_FEEDBACK ifeed,ELMNT per)815 check_indi_links (IMPORT_FEEDBACK ifeed, ELMNT per)
816 {
817 BOOLEAN jm, jf, bm, bf;
818 if (Male(per) > 1)
819 handle_warn(ifeed
820 , _("Line %d: Person %s has multiple father links.")
821 , Line(per), Key(per));
822 if (Fmle(per) > 1)
823 handle_warn(ifeed
824 , _("Line %d: Person %s has multiple mother links.")
825 , Line(per), Key(per));
826 if (Line(per) == 0) {
827 handle_err(ifeed, qSundper, Key(per));
828 return;
829 }
830 jm = Sex(per) & IS_MALE;
831 jf = Sex(per) & IS_FEMALE;
832 bm = Sex(per) & BE_MALE;
833 bf = Sex(per) & BE_FEMALE;
834 if (jm && jf)
835 handle_err(ifeed
836 , _("Line %d: Person %s is both male and female.")
837 , Line(per), Key(per));
838 if (jm && bf)
839 handle_err(ifeed
840 , _("Line %d: Person %s is male but must be female.")
841 , Line(per), Key(per));
842 if (jf && bm)
843 handle_err(ifeed
844 , _("Line %d: Person %s is female but must be male.")
845 , Line(per), Key(per));
846 if (bm && bf)
847 handle_err(ifeed
848 , _("Line %d: Person %s is implied to be both male and female.")
849 , Line(per), Key(per));
850 }
851 /*======================================
852 * check_fam_links -- Check family links
853 *====================================*/
854 static void
check_fam_links(IMPORT_FEEDBACK ifeed,ELMNT fam)855 check_fam_links (IMPORT_FEEDBACK ifeed, ELMNT fam)
856 {
857 if (Line(fam) == 0) handle_err(ifeed, qSundfam, Key(fam));
858 if (Male(fam) > 1)
859 handle_warn(ifeed
860 , _("Line %d: Family %s has multiple husband links.")
861 , Line(fam), Key(fam));
862 if (Fmle(fam) > 1)
863 handle_warn(ifeed
864 , _("Line %d: Family %s has multiple wife links.")
865 , Line(fam), Key(fam));
866
867 }
868 /*======================================
869 * check_even_links -- Check event links
870 *====================================*/
871 static void
check_even_links(IMPORT_FEEDBACK ifeed,ELMNT evn)872 check_even_links (IMPORT_FEEDBACK ifeed, ELMNT evn)
873 {
874 if (Line(evn) == 0) handle_err(ifeed, qSundevn, Key(evn));
875 }
876 /*=======================================
877 * check_sour_links -- Check source links
878 *=====================================*/
879 static void
check_sour_links(IMPORT_FEEDBACK ifeed,ELMNT src)880 check_sour_links (IMPORT_FEEDBACK ifeed, ELMNT src)
881 {
882 if (Line(src) == 0) handle_err(ifeed, qSundsrc, Key(src));
883 }
884 /*======================================
885 * check_othr_links -- Check other links
886 *====================================*/
887 static void
check_othr_links(IMPORT_FEEDBACK ifeed,ELMNT otr)888 check_othr_links (IMPORT_FEEDBACK ifeed, ELMNT otr)
889 {
890 if (Line(otr) == 0) handle_err(ifeed, qSundrec, Key(otr));
891 }
892 /*=======================================
893 * handle_value -- Handle arbitrary value
894 *=====================================*/
895 static void
handle_value(STRING val,INT line)896 handle_value (STRING val, INT line)
897 {
898 ELMNT el;
899 STRING xref;
900
901 if (rec_type == IGNR_REC) return;
902 if (!pointer_value(val)) return;
903 xref = rmvat(val);
904 if (xref_to_index(xref) != -1) return;
905 el = create_elmnt(UNKN_REC, xref);
906 New(el) = NULL;
907 Line(el) = line;
908 add_to_structures(xref, el);
909 }
910 /*==================================
911 * handle_err -- Handle GEDCOM error
912 *================================*/
913 static void
handle_err(IMPORT_FEEDBACK ifeed,STRING fmt,...)914 handle_err (IMPORT_FEEDBACK ifeed, STRING fmt, ...)
915 {
916 ZSTR zstr=zs_new();
917
918 if (openlog()) {
919 va_list args;
920 fprintf(f_flog, "%s: ", _("error"));
921 va_start(args, fmt);
922 vfprintf(f_flog, fmt, args);
923 va_end(args);
924 fprintf(f_flog, "\n");
925 }
926
927 ++num_errors;
928 zs_setf(zstr, _pl("%6d Error", "%6d Errors", num_errors), num_errors);
929 if (f_logopen)
930 zs_appf(zstr, _(" (see log file <%s>)"), f_logpath);
931 else
932 zs_apps(zstr, _(" (no log file)"));
933
934 if (ifeed && ifeed->validation_error_fnc)
935 (*ifeed->validation_error_fnc)(zs_str(zstr));
936 zs_free(&zstr);
937 }
938 /*=====================================
939 * handle_warn -- Handle GEDCOM warning
940 *===================================*/
941 static void
handle_warn(IMPORT_FEEDBACK ifeed,STRING fmt,...)942 handle_warn (IMPORT_FEEDBACK ifeed, STRING fmt, ...)
943 {
944 ZSTR zstr=zs_new();
945
946 if (openlog()) {
947 va_list args;
948 fprintf(f_flog, "%s: ", _("warning"));
949 va_start(args, fmt);
950 vfprintf(f_flog, fmt, args);
951 va_end(args);
952 fprintf(f_flog, "\n");
953 }
954
955 ++num_warns;
956 zs_setf(zstr, _pl("%6d Warning", "%6d Warnings", num_warns), num_warns);
957 if (f_logopen)
958 zs_appf(zstr, _(" (see log file <%s>)"), f_logpath);
959 else
960 zs_appf(zstr, _(" (no log file)"));
961 if (ifeed && ifeed->validation_warning_fnc)
962 (*ifeed->validation_warning_fnc)(zs_str(zstr));
963 zs_free(&zstr);
964 }
965 /*=====================================
966 * openlog -- open import error log if not already open
967 *===================================*/
968 static BOOLEAN
openlog(void)969 openlog (void)
970 {
971 if (f_logopen)
972 return TRUE;
973
974 ASSERT(!f_flog);
975 if (!f_logpath[0])
976 return FALSE;
977
978 f_flog = fopen(f_logpath, LLWRITETEXT);
979 f_logopen = (f_flog != 0);
980 return f_logopen;
981 }
982 /*=========================================
983 * xref_to_index - Convert pointer to index
984 *=======================================*/
985 INT
xref_to_index(STRING xref)986 xref_to_index (STRING xref)
987 {
988 BOOLEAN there;
989 INT dex = valueofbool_int(convtab, xref, &there);
990 return there ? dex : -1;
991 }
992 /*=========================================================
993 * add_to_structures -- Add new elements to data structures
994 *=======================================================*/
995 static INT
add_to_structures(STRING xref,ELMNT el)996 add_to_structures (STRING xref, ELMNT el)
997 {
998 INT i, n;
999
1000 if ((n = struct_len) >= struct_max) {
1001 ELMNT *newi = (ELMNT *) stdalloc(sizeof(ELMNT)*
1002 (n + 10000));
1003 for (i = 0; i < n; i++)
1004 newi[i] = index_data[i];
1005 if (n) stdfree(index_data);
1006 index_data = newi;
1007 struct_max += 10000;
1008 }
1009 i = struct_len;
1010 index_data[i] = el;
1011 insert_table_int(convtab, xref, struct_len++);
1012 return struct_len - 1;
1013 }
1014 /*========================================================
1015 * clear_structures -- Clear GEDCOM import data structures
1016 *======================================================*/
1017 static void
clear_structures(void)1018 clear_structures (void)
1019 {
1020 INT i;
1021
1022 if (convtab) {
1023 /* elements are destroyed below, because
1024 index_data points to them */
1025 destroy_table(convtab);
1026 convtab = NULL;
1027 }
1028 for (i = 0; i < struct_len; i++) {
1029 ELMNT el = index_data[i];
1030 index_data[i] = 0;
1031 free_elmnt(el);
1032 }
1033 struct_len = 0;
1034 }
1035 /*=====================================
1036 * set_import_log -- Specify where import errors logged
1037 *===================================*/
1038 static void
set_import_log(STRING logpath)1039 set_import_log (STRING logpath)
1040 {
1041 if (!logpath)
1042 logpath = "";
1043 llstrncpy(f_logpath, logpath, sizeof(f_logpath), uu8);
1044 }
1045 /*============================================================
1046 * scan_header -- Scan header of GEDCOM record
1047 * collecting metadata as found (metadatatab is a FREEBOTH tab)
1048 * Created: 2003-02-03 (Perry Rapp)
1049 *==========================================================*/
1050 BOOLEAN
scan_header(FILE * fp,TABLE metadatatab,ZSTR * zerr)1051 scan_header (FILE * fp, TABLE metadatatab, ZSTR * zerr)
1052 {
1053 STRING parents[2] = { 0, 0 };
1054 INT linno, head=0, lev=-1, curlev,i;
1055 INT lastoff=0;
1056 ZSTR zpath = zs_new();
1057 *zerr = 0;
1058 for (linno=1; 1; ++linno) {
1059 XLAT xlat=0;
1060 /* no codeset translation yet, b/c we've not found the file's
1061 encoding declaration yet */
1062 INT rc;
1063 STRING xref, tag, val, msg;
1064 lastoff = ftell(fp);
1065 curlev = lev;
1066 if (linno==500) {
1067 *zerr = zs_newf(_("Processed %d lines without finding end of HEAD"), linno);
1068 break;
1069 }
1070 rc = file_to_line(fp, xlat, &lev, &xref, &tag, &val, &msg);
1071 if (rc==DONE) {
1072 *zerr = zs_newf(_("End of file at line %d"), linno);
1073 break;
1074 }
1075 if (rc==ERROR) {
1076 *zerr = zs_newf(_("Error at line %d: %s"), linno, msg);
1077 break;
1078 }
1079 if (lev < 0 || lev > curlev+1) {
1080 *zerr = zs_newf(_("Bad level at line %d"), linno);
1081 break;
1082 }
1083 if (lev==0) {
1084 if (eqstr(tag, "HEAD")) {
1085 if (head) {
1086 *zerr = zs_newf(_("Duplicate HEAD line at line %d"), linno);
1087 break;
1088 } else {
1089 head=1;
1090 }
1091 } else if (!head) {
1092 *zerr = zs_newf(_("Missing HEAD line at line %d"), linno);
1093 break;
1094 } else {
1095 fseek(fp, lastoff, SEEK_SET);
1096 flineno--;
1097 /* finished head */
1098 break;
1099 }
1100 }
1101 if (lev < 3 && tag && tag[0] && val && val[0]) { /* we don't care about anything beyond level 2 */
1102 zs_clear(zpath);
1103 for (i=1; i<lev && i<ARRSIZE(parents); ++i)
1104 append_path(zpath, '.', parents[i-1]);
1105 append_path(zpath, '.', tag);
1106 insert_table_str(metadatatab, zs_str(zpath), val);
1107 }
1108 if (lev>0 && lev-1<ARRSIZE(parents))
1109 strupdate(&parents[lev-1], tag);
1110 /* clear any obsolete parents */
1111 for (i=lev; i<ARRSIZE(parents); ++i) {
1112 strupdate(&parents[i], 0);
1113 }
1114 }
1115
1116 for (i=0; i<ARRSIZE(parents); ++i) {
1117 strupdate(&parents[i], 0);
1118 }
1119 zs_free(&zpath);
1120 return !(*zerr);
1121 }
1122 /*============================================================
1123 * append_path -- Add path to end of string, prefixing with delimiter
1124 * if string non-empty
1125 * Created: 2003-02-03 (Perry Rapp)
1126 *==========================================================*/
1127 static void
append_path(ZSTR zstr,char delim,CNSTRING str)1128 append_path (ZSTR zstr, char delim, CNSTRING str)
1129 {
1130 if (zs_len(zstr))
1131 zs_appc(zstr, delim);
1132 zs_apps(zstr, str);
1133 }
1134 /*===================================================
1135 * validate_get_warning_count -- How many warnings were found ?
1136 *=================================================*/
1137 INT
validate_get_warning_count(void)1138 validate_get_warning_count (void)
1139 {
1140 return num_warns;
1141 }
1142 /*===================================================
1143 * validate_end_import -- Free any memory left after import finished
1144 *=================================================*/
1145 void
validate_end_import(void)1146 validate_end_import (void)
1147 {
1148 clear_structures();
1149 }
1150