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