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 int compat_state[C_NR_OF_RULES];
140 /* Compatibility handling */
142 void gedcom_set_compat_handling(int enable_compat)
144 compat_enabled = enable_compat;
147 void enable_compat_msg(const char* program_name, int version)
150 gedcom_warning(_("Enabling compatibility with '%s', version %d"),
151 program_name, version);
153 gedcom_warning(_("Enabling compatibility with '%s'"),
157 int program_equal(const char* program, const char* compare)
159 return !strncmp(program, compare, strlen(compare)+1);
162 int program_equal_continued(const char* program, const char* compare)
164 size_t len = strlen(compare);
165 int result = strncmp(program, compare, len);
167 if (strlen(program) > len)
168 set_compatibility_version(program + len);
173 void set_compatibility_program(const char* program)
175 compatibility_program = 0;
176 if (compat_enabled) {
177 if (program_equal(program, "ftree")) {
178 compatibility_program = CP_FTREE;
180 else if (program_equal_continued(program, "LIFELINES")) {
181 compatibility_program = CP_LIFELINES;
183 else if (program_equal_continued(program, "PAF")) {
184 compatibility_program = CP_PAF;
186 else if (program_equal(program, "FamilyOrigins")) {
187 compatibility_program = CP_FAMORIG;
189 else if (program_equal(program, "EasyTree")) {
190 compatibility_program = CP_EASYTREE;
195 void compute_compatibility()
197 /* Reinitialize compatibility */
200 default_charset = "";
202 for (i = 0; i < C_NR_OF_RULES; i++)
205 switch (compatibility_program) {
207 if (compatibility_version >= 20000 && compatibility_version < 30000) {
208 compatibility = C_PAF2;
211 if (compatibility_version >= 40000 && compatibility_version < 50000) {
212 compatibility = C_PAF4;
215 else if (compatibility_version >= 50000) {
216 compatibility = C_PAF5;
221 compatibility = data[compatibility_program].default_compat;
225 default_charset = data[compatibility_program].default_charset;
226 enable_compat_msg(data[compatibility_program].name, version);
230 void set_compatibility_version(const char* version)
232 if (compat_enabled) {
233 unsigned int major=0, minor=0, patch=0;
236 result = sscanf(version, " %u.%u.%u", &major, &minor, &patch);
238 gedcom_debug_print("Setting compat version to %u.%u.%u",
239 major, minor, patch);
240 compatibility_version = major * 10000 + minor * 100 + patch;
245 int compat_mode(Compat_rule rule)
247 return (compat_matrix[rule] & compatibility);
250 /********************************************************************/
252 /********************************************************************/
254 void compat_generate_submitter_link(Gedcom_ctxt parent)
256 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_USED,
258 struct tag_struct ts;
263 gedcom_warning(_("Adding link to submitter record with xref '%s'"),
265 self = start_element(ELT_HEAD_SUBM,
266 parent, 1, ts, SUBMITTER_LINK,
267 GEDCOM_MAKE_XREF_PTR(val1, xr));
268 end_element(ELT_HEAD_SUBM, parent, self, NULL);
269 compat_state[C_NO_SUBMITTER] = 1;
272 void compat_generate_submitter()
274 if (compat_state[C_NO_SUBMITTER]) {
275 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_DEFINED,
277 struct tag_struct ts;
278 Gedcom_ctxt self1, self2;
280 /* first generate "0 SUBM" */
283 self1 = start_record(REC_SUBM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
284 NULL, GEDCOM_MAKE_NULL(val2));
286 /* then generate "1 NAME ..." */
289 self2 = start_element(ELT_SUBM_NAME, self1, 1, ts, DEFAULT_SUBMITTER_NAME,
290 GEDCOM_MAKE_STRING(val1, DEFAULT_SUBMITTER_NAME));
292 /* close "1 NAME ..." */
293 end_element(ELT_SUBM_NAME, self1, self2, NULL);
296 end_record(REC_SUBM, self1, NULL);
297 compat_state[C_NO_SUBMITTER] = 0;
301 /********************************************************************/
303 /********************************************************************/
305 void compat_generate_gedcom(Gedcom_ctxt parent)
307 struct tag_struct ts;
308 Gedcom_ctxt self1, self2;
310 /* first generate "1 GEDC" */
313 self1 = start_element(ELT_HEAD_GEDC, parent, 1, ts, NULL,
314 GEDCOM_MAKE_NULL(val1));
316 /* then generate "2 VERS <DEFAULT_GEDC_VERS>" */
319 self2 = start_element(ELT_HEAD_GEDC_VERS, self1, 2, ts,
321 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_VERS));
324 end_element(ELT_HEAD_GEDC_VERS, self1, self2, NULL);
326 /* then generate "2 FORM <DEFAULT_GEDCOM_FORM> */
327 compat_generate_gedcom_form(self1);
330 end_element(ELT_HEAD_GEDC, parent, self1, NULL);
333 /********************************************************************/
335 /********************************************************************/
337 void compat_generate_gedcom_form(Gedcom_ctxt parent)
339 struct tag_struct ts;
342 /* generate "2 FORM <DEFAULT_GEDCOM_FORM> */
345 self = start_element(ELT_HEAD_GEDC_FORM, parent, 2, ts,
347 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
350 end_element(ELT_HEAD_GEDC_FORM, parent, self, NULL);
354 /********************************************************************/
356 /********************************************************************/
358 int compat_generate_char(Gedcom_ctxt parent)
360 struct tag_struct ts;
364 /* first generate "1 CHAR <DEFAULT_CHAR>" */
368 /* Must strdup, because default_charset is const char */
369 charset = strdup(default_charset);
373 self1 = start_element(ELT_HEAD_CHAR, parent, 1, ts, charset,
374 GEDCOM_MAKE_STRING(val1, charset));
378 end_element(ELT_HEAD_CHAR, parent, self1, NULL);
380 if (open_conv_to_internal(default_charset) == 0)
386 /********************************************************************/
388 /********************************************************************/
390 Gedcom_ctxt compat_generate_resi_start(Gedcom_ctxt parent)
393 struct tag_struct ts;
397 self = start_element(ELT_SUB_INDIV_RESI, parent, 1, ts, NULL,
398 GEDCOM_MAKE_NULL(val1));
402 void compat_generate_resi_end(Gedcom_ctxt parent, Gedcom_ctxt self)
404 end_element(ELT_SUB_INDIV_RESI, parent, self, NULL);
407 /********************************************************************/
409 /********************************************************************/
411 int is_551_tag(const char* tag)
413 if (strncmp(tag, "EMAIL", 6))
415 else if (strncmp(tag, "FONE", 5))
417 else if (strncmp(tag, "ROMN", 5))
423 int compat_check_551_tag(const char* tag, struct safe_buffer* b)
425 if (is_551_tag(tag)) {
427 SAFE_BUF_ADDCHAR(b, '_');
428 safe_buf_append(b, tag);
429 gedcom_warning(_("Converting 5.5.1 tag '%s' to standard 5.5 user tag '%s'"),
430 tag, get_buf_string(b));
437 /********************************************************************/
439 /********************************************************************/
441 void compat_generate_slgc_famc_link(Gedcom_ctxt parent)
443 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_USED,
445 struct tag_struct ts;
450 gedcom_warning(_("Adding link to family record with xref '%s'"),
452 self = start_element(ELT_SUB_LIO_SLGC_FAMC,
453 parent, 2, ts, SLGC_FAMC_LINK,
454 GEDCOM_MAKE_XREF_PTR(val1, xr));
455 end_element(ELT_SUB_LIO_SLGC_FAMC, parent, self, NULL);
456 compat_state[C_NO_SLGC_FAMC]++;
459 void compat_generate_slgc_famc_fam()
461 /* If bigger than 1, then the FAM record has already been generated */
462 if (compat_state[C_NO_SLGC_FAMC] == 1) {
463 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_DEFINED,
465 struct tag_struct ts;
468 /* generate "0 FAM" */
471 self = start_record(REC_FAM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
472 NULL, GEDCOM_MAKE_NULL(val2));
475 end_record(REC_FAM, self, NULL);
479 /********************************************************************/
481 /********************************************************************/
483 int compat_check_subm_comm(const char* tag, const char* parent_tag,
484 struct safe_buffer* b)
486 if (!strcmp(tag, "COMM") && !strcmp(parent_tag, "SUBM")) {
488 SAFE_BUF_ADDCHAR(b, '_');
489 safe_buf_append(b, tag);
490 gedcom_warning(_("Converting non-standard tag '%s' to user tag '%s'"),
491 tag, get_buf_string(b));
492 compat_state[C_SUBM_COMM] = 1;
499 void compat_close_subm_comm()
501 compat_state[C_SUBM_COMM] = 0;
504 int compat_check_subm_comm_cont(const char* tag)
506 if (compat_state[C_SUBM_COMM] && !strcmp(tag, "CONT")) {
507 compat_state[C_SUBM_COMM] = 2;
514 Gedcom_ctxt compat_subm_comm_cont_start(Gedcom_ctxt parent, char* str)
516 Gedcom_ctxt self = NULL;
517 struct tag_struct ts;
519 if (compat_state[C_SUBM_COMM] == 2) {
522 self = start_element(ELT_USER, parent, 2, ts, str, &val2);
528 void compat_subm_comm_cont_end(Gedcom_ctxt parent, Gedcom_ctxt self)
530 if (compat_state[C_SUBM_COMM] == 2) {
531 end_element(ELT_USER, parent, self, NULL);
532 compat_state[C_SUBM_COMM] = 1;