1 /* Compatibility handling for the GEDCOM parser.
2 Copyright (C) 2001, 2002 The Genes Development Team
3 This file is part of the Gedcom parser library.
4 Contributed by Peter Verthez <Peter.Verthez@advalvas.be>, 2001.
6 The Gedcom parser library is free software; you can redistribute it
7 and/or modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The Gedcom parser library is distributed in the hope that it will be
12 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the Gedcom parser library; if not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25 #include "interface.h"
29 #include "gedcom_internal.h"
32 int compat_enabled = 1;
33 int compatibility = 0;
34 int compatibility_program = 0;
35 int compatibility_version = 0;
36 const char* default_charset = "";
38 #define SUBMITTER_LINK "@__COMPAT__SUBM__@"
39 #define SLGC_FAMC_LINK "@__COMPAT__FAM_SLGC__@"
40 #define DEFAULT_SUBMITTER_NAME "Submitter"
41 #define DEFAULT_GEDCOM_VERS "5.5"
42 #define DEFAULT_GEDCOM_FORM "LINEAGE-LINKED"
47 const char* default_charset;
50 enum _COMPAT_PROGRAM {
68 struct program_data data[] = {
69 /* NULL */ { "", 0, "" },
70 /* CP_FTREE */ { "ftree", C_FTREE, "" },
71 /* CP_LIFELINES */ { "Lifelines", C_LIFELINES, "ANSI" },
72 /* CP_PAF */ { "Personal Ancestral File", C_PAF5, "" },
73 /* CP_FAMORIG */ { "Family Origins", C_FAMORIG, "" },
74 /* CP_EASYTREE */ { "EasyTree", C_EASYTREE, "" }
77 /* Incompatibility list (with GEDCOM 5.5):
80 - no submitter record, no submitter link in the header
81 - INDI.ADDR instead of INDI.RESI.ADDR
82 - NOTE doesn't have a value
85 - no submitter record, no submitter link in the header
86 - no GEDC field in the header
87 - no CHAR field in the header
88 - HEAD.TIME instead of HEAD.DATE.TIME (will be ignored here)
89 - '@' not written as '@@' in values
90 - lots of missing required values
92 - Personal Ancestral File 5:
93 - '@' not written as '@@' in values
94 - some 5.5.1 (draft) tags are used: EMAIL, FONE, ROMN
95 - no FAMC field in SLGC
96 - uses tab character (will be converted to 8 spaces here)
98 - Personal Ancestral File 2:
99 - '@' not written as '@@' in values
100 - COMM tag in submitter record
101 - double dates written as e.g. '1815/1816' instead of '1815/16'
104 - '@' not written as '@@' in values
105 - CONC needs an extra space
109 - no submitter link in the header
110 - NOTE doesn't have a value
111 - NOTE.NOTE instead of NOTE.COND
113 - Personal Ancestral File 4:
114 - '@' not written as '@@' in values
117 int compat_matrix[] =
119 /* C_NO_SUBMITTER */ C_FTREE | C_LIFELINES | C_PAF2 | C_EASYTREE,
120 /* C_INDI_ADDR */ C_FTREE,
121 /* C_NOTE_NO_VALUE */ C_FTREE | C_EASYTREE,
122 /* C_NO_GEDC */ C_LIFELINES | C_PAF2,
123 /* C_NO_CHAR */ C_LIFELINES,
124 /* C_HEAD_TIME */ C_LIFELINES,
125 /* C_NO_DOUBLE_AT */ C_LIFELINES | C_PAF5 | C_PAF2 | C_FAMORIG
127 /* C_NO_REQUIRED_VALUES */ C_LIFELINES | C_PAF5,
128 /* C_551_TAGS */ C_PAF5,
129 /* C_NO_SLGC_FAMC */ C_PAF5,
130 /* C_SUBM_COMM */ C_PAF2,
131 /* C_DOUBLE_DATES_4 */ C_PAF2 | C_PAF5 | C_PAF4,
132 /* C_CONC_NEEDS_SPACE */ C_FAMORIG,
133 /* C_NO_GEDC_FORM */ C_EASYTREE,
134 /* C_NOTE_NOTE */ C_EASYTREE,
135 /* C_TAB_CHARACTER */ C_PAF5
138 union _COMPAT_STATE {
141 } compat_state[C_NR_OF_RULES];
143 /* Compatibility handling */
145 void gedcom_set_compat_handling(int enable_compat)
147 compat_enabled = enable_compat;
150 void enable_compat_msg(const char* program_name, int version)
153 gedcom_warning(_("Enabling compatibility with '%s', version %d"),
154 program_name, version);
156 gedcom_warning(_("Enabling compatibility with '%s'"),
160 int program_equal(const char* program, const char* compare)
162 return !strncmp(program, compare, strlen(compare)+1);
165 int program_equal_continued(const char* program, const char* compare)
167 size_t len = strlen(compare);
168 int result = strncmp(program, compare, len);
170 if (strlen(program) > len)
171 set_compatibility_version(program + len);
176 void set_compatibility_program(const char* program)
178 compatibility_program = 0;
179 if (compat_enabled) {
180 if (program_equal(program, "ftree")) {
181 compatibility_program = CP_FTREE;
183 else if (program_equal_continued(program, "LIFELINES")) {
184 compatibility_program = CP_LIFELINES;
186 else if (program_equal_continued(program, "PAF")) {
187 compatibility_program = CP_PAF;
189 else if (program_equal(program, "FamilyOrigins")) {
190 compatibility_program = CP_FAMORIG;
192 else if (program_equal(program, "EasyTree")) {
193 compatibility_program = CP_EASYTREE;
198 void compute_compatibility()
200 /* Reinitialize compatibility */
203 default_charset = "";
205 for (i = 0; i < C_NR_OF_RULES; i++)
206 compat_state[i].i = 0;
208 switch (compatibility_program) {
210 if (compatibility_version >= 20000 && compatibility_version < 30000) {
211 compatibility = C_PAF2;
214 if (compatibility_version >= 40000 && compatibility_version < 50000) {
215 compatibility = C_PAF4;
218 else if (compatibility_version >= 50000) {
219 compatibility = C_PAF5;
224 compatibility = data[compatibility_program].default_compat;
228 default_charset = data[compatibility_program].default_charset;
229 enable_compat_msg(data[compatibility_program].name, version);
233 void set_compatibility_version(const char* version)
235 if (compat_enabled) {
236 unsigned int major=0, minor=0, patch=0;
239 result = sscanf(version, " %u.%u.%u", &major, &minor, &patch);
241 gedcom_debug_print("Setting compat version to %u.%u.%u",
242 major, minor, patch);
243 compatibility_version = major * 10000 + minor * 100 + patch;
248 int compat_mode(Compat_rule rule)
250 return (compat_matrix[rule] & compatibility);
253 /********************************************************************/
255 /********************************************************************/
257 void compat_generate_submitter_link(Gedcom_ctxt parent)
259 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_USED,
261 struct tag_struct ts;
266 gedcom_warning(_("Adding link to submitter record with xref '%s'"),
268 self = start_element(ELT_HEAD_SUBM,
269 parent, 1, ts, SUBMITTER_LINK,
270 GEDCOM_MAKE_XREF_PTR(val1, xr));
271 end_element(ELT_HEAD_SUBM, parent, self, NULL);
272 compat_state[C_NO_SUBMITTER].i = 1;
275 void compat_generate_submitter()
277 if (compat_state[C_NO_SUBMITTER].i) {
278 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_DEFINED,
280 struct tag_struct ts;
281 Gedcom_ctxt self1, self2;
283 /* first generate "0 SUBM" */
286 self1 = start_record(REC_SUBM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
287 NULL, GEDCOM_MAKE_NULL(val2));
289 /* then generate "1 NAME ..." */
292 self2 = start_element(ELT_SUBM_NAME, self1, 1, ts, DEFAULT_SUBMITTER_NAME,
293 GEDCOM_MAKE_STRING(val1, DEFAULT_SUBMITTER_NAME));
295 /* close "1 NAME ..." */
296 end_element(ELT_SUBM_NAME, self1, self2, NULL);
299 end_record(REC_SUBM, self1, NULL);
300 compat_state[C_NO_SUBMITTER].i = 0;
304 /********************************************************************/
306 /********************************************************************/
308 void compat_generate_gedcom(Gedcom_ctxt parent)
310 struct tag_struct ts;
311 Gedcom_ctxt self1, self2;
313 /* first generate "1 GEDC" */
316 self1 = start_element(ELT_HEAD_GEDC, parent, 1, ts, NULL,
317 GEDCOM_MAKE_NULL(val1));
319 /* then generate "2 VERS <DEFAULT_GEDC_VERS>" */
322 self2 = start_element(ELT_HEAD_GEDC_VERS, self1, 2, ts,
324 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_VERS));
327 end_element(ELT_HEAD_GEDC_VERS, self1, self2, NULL);
329 /* then generate "2 FORM <DEFAULT_GEDCOM_FORM> */
330 compat_generate_gedcom_form(self1);
333 end_element(ELT_HEAD_GEDC, parent, self1, NULL);
336 /********************************************************************/
338 /********************************************************************/
340 void compat_generate_gedcom_form(Gedcom_ctxt parent)
342 struct tag_struct ts;
345 /* generate "2 FORM <DEFAULT_GEDCOM_FORM> */
348 self = start_element(ELT_HEAD_GEDC_FORM, parent, 2, ts,
350 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
353 end_element(ELT_HEAD_GEDC_FORM, parent, self, NULL);
357 /********************************************************************/
359 /********************************************************************/
361 int compat_generate_char(Gedcom_ctxt parent)
363 struct tag_struct ts;
367 /* first generate "1 CHAR <DEFAULT_CHAR>" */
371 /* Must strdup, because default_charset is const char */
372 charset = strdup(default_charset);
376 self1 = start_element(ELT_HEAD_CHAR, parent, 1, ts, charset,
377 GEDCOM_MAKE_STRING(val1, charset));
381 end_element(ELT_HEAD_CHAR, parent, self1, NULL);
383 if (open_conv_to_internal(default_charset) == 0)
389 /********************************************************************/
391 /********************************************************************/
393 void compat_save_head_date_context(Gedcom_ctxt parent)
395 compat_state[C_HEAD_TIME].vp = parent;
398 Gedcom_ctxt compat_generate_head_time_start(int level, struct tag_struct ts,
401 Gedcom_ctxt parent = compat_state[C_HEAD_TIME].vp;
405 return start_element(ELT_HEAD_DATE_TIME,
406 parent, level, ts, value,
407 GEDCOM_MAKE_STRING(val1, value));
412 void compat_generate_head_time_end(Gedcom_ctxt self)
414 Gedcom_ctxt parent = compat_state[C_HEAD_TIME].vp;
416 end_element(ELT_HEAD_DATE_TIME,
417 parent, self, GEDCOM_MAKE_NULL(val1));
420 /********************************************************************/
422 /********************************************************************/
424 Gedcom_ctxt compat_generate_resi_start(Gedcom_ctxt parent)
427 struct tag_struct ts;
431 self = start_element(ELT_SUB_INDIV_RESI, parent, 1, ts, NULL,
432 GEDCOM_MAKE_NULL(val1));
436 void compat_generate_resi_end(Gedcom_ctxt parent, Gedcom_ctxt self)
438 end_element(ELT_SUB_INDIV_RESI, parent, self, NULL);
441 /********************************************************************/
443 /********************************************************************/
445 int is_551_tag(const char* tag)
447 if (strncmp(tag, "EMAIL", 6))
449 else if (strncmp(tag, "FONE", 5))
451 else if (strncmp(tag, "ROMN", 5))
457 int compat_check_551_tag(const char* tag, struct safe_buffer* b)
459 if (is_551_tag(tag)) {
461 SAFE_BUF_ADDCHAR(b, '_');
462 safe_buf_append(b, tag);
463 gedcom_warning(_("Converting 5.5.1 tag '%s' to standard 5.5 user tag '%s'"),
464 tag, get_buf_string(b));
471 /********************************************************************/
473 /********************************************************************/
475 void compat_generate_slgc_famc_link(Gedcom_ctxt parent)
477 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_USED,
479 struct tag_struct ts;
484 gedcom_warning(_("Adding link to family record with xref '%s'"),
486 self = start_element(ELT_SUB_LIO_SLGC_FAMC,
487 parent, 2, ts, SLGC_FAMC_LINK,
488 GEDCOM_MAKE_XREF_PTR(val1, xr));
489 end_element(ELT_SUB_LIO_SLGC_FAMC, parent, self, NULL);
490 compat_state[C_NO_SLGC_FAMC].i++;
493 void compat_generate_slgc_famc_fam()
495 /* If bigger than 1, then the FAM record has already been generated */
496 if (compat_state[C_NO_SLGC_FAMC].i == 1) {
497 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_DEFINED,
499 struct tag_struct ts;
502 /* generate "0 FAM" */
505 self = start_record(REC_FAM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
506 NULL, GEDCOM_MAKE_NULL(val2));
509 end_record(REC_FAM, self, NULL);
513 /********************************************************************/
515 /********************************************************************/
517 int compat_check_subm_comm(const char* tag, const char* parent_tag,
518 struct safe_buffer* b)
520 if (!strcmp(tag, "COMM") && !strcmp(parent_tag, "SUBM")) {
522 SAFE_BUF_ADDCHAR(b, '_');
523 safe_buf_append(b, tag);
524 gedcom_warning(_("Converting non-standard tag '%s' to user tag '%s'"),
525 tag, get_buf_string(b));
526 compat_state[C_SUBM_COMM].i = 1;
533 void compat_close_subm_comm()
535 compat_state[C_SUBM_COMM].i = 0;
538 int compat_check_subm_comm_cont(const char* tag)
540 if (compat_state[C_SUBM_COMM].i && !strcmp(tag, "CONT")) {
541 compat_state[C_SUBM_COMM].i = 2;
548 Gedcom_ctxt compat_subm_comm_cont_start(Gedcom_ctxt parent, char* str)
550 Gedcom_ctxt self = NULL;
551 struct tag_struct ts;
553 if (compat_state[C_SUBM_COMM].i == 2) {
556 self = start_element(ELT_USER, parent, 2, ts, str, &val2);
562 void compat_subm_comm_cont_end(Gedcom_ctxt parent, Gedcom_ctxt self)
564 if (compat_state[C_SUBM_COMM].i == 2) {
565 end_element(ELT_USER, parent, self, NULL);
566 compat_state[C_SUBM_COMM].i = 1;