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 {
67 struct program_data data[] = {
68 /* NULL */ { "", 0, "" },
69 /* CP_FTREE */ { "ftree", C_FTREE, "" },
70 /* CP_LIFELINES */ { "Lifelines", C_LIFELINES, "ANSI" },
71 /* CP_PAF */ { "Personal Ancestral File", C_PAF5, "" },
72 /* CP_FAMORIG */ { "Family Origins", C_FAMORIG, "" },
73 /* CP_EASYTREE */ { "EasyTree", C_EASYTREE, "" }
76 /* Incompatibility list (with GEDCOM 5.5):
79 - no submitter record, no submitter link in the header
80 - INDI.ADDR instead of INDI.RESI.ADDR
81 - NOTE doesn't have a value
84 - no submitter record, no submitter link in the header
85 - no GEDC field in the header
86 - no CHAR field in the header
87 - HEAD.TIME instead of HEAD.DATE.TIME (will be ignored here)
88 - '@' not written as '@@' in values
89 - lots of missing required values
91 - Personal Ancestral File 5:
92 - '@' not written as '@@' in values
93 - some 5.5.1 (draft) tags are used: EMAIL, FONE, ROMN
94 - no FAMC field in SLGC
96 - Personal Ancestral File 2:
97 - '@' not written as '@@' in values
98 - COMM tag in submitter record
99 - double dates written as e.g. '1815/1816' instead of '1815/16'
102 - '@' not written as '@@' in values
103 - CONC needs an extra space
107 - no submitter link in the header
108 - NOTE doesn't have a value
109 - NOTE.NOTE instead of NOTE.COND
112 int compat_matrix[] =
114 /* C_NO_SUBMITTER */ C_FTREE | C_LIFELINES | C_PAF2 | C_EASYTREE,
115 /* C_INDI_ADDR */ C_FTREE,
116 /* C_NOTE_NO_VALUE */ C_FTREE | C_EASYTREE,
117 /* C_NO_GEDC */ C_LIFELINES | C_PAF2,
118 /* C_NO_CHAR */ C_LIFELINES,
119 /* C_HEAD_TIME */ C_LIFELINES,
120 /* C_NO_DOUBLE_AT */ C_LIFELINES | C_PAF5 | C_PAF2 | C_FAMORIG,
121 /* C_NO_REQUIRED_VALUES */ C_LIFELINES,
122 /* C_551_TAGS */ C_PAF5,
123 /* C_NO_SLGC_FAMC */ C_PAF5,
124 /* C_SUBM_COMM */ C_PAF2,
125 /* C_DOUBLE_DATES_4 */ C_PAF2,
126 /* C_CONC_NEEDS_SPACE */ C_FAMORIG,
127 /* C_NO_GEDC_FORM */ C_EASYTREE,
128 /* C_NOTE_NOTE */ C_EASYTREE
131 int compat_state[C_NR_OF_RULES];
133 /* Compatibility handling */
135 void gedcom_set_compat_handling(int enable_compat)
137 compat_enabled = enable_compat;
140 void enable_compat_msg(const char* program_name, int version)
143 gedcom_warning(_("Enabling compatibility with '%s', version %d"),
144 program_name, version);
146 gedcom_warning(_("Enabling compatibility with '%s'"),
150 int program_equal(const char* program, const char* compare)
152 return !strncmp(program, compare, strlen(compare)+1);
155 int program_equal_continued(const char* program, const char* compare)
157 size_t len = strlen(compare);
158 int result = strncmp(program, compare, len);
160 if (strlen(program) > len)
161 set_compatibility_version(program + len);
166 void set_compatibility_program(const char* program)
168 compatibility_program = 0;
169 if (compat_enabled) {
170 if (program_equal(program, "ftree")) {
171 compatibility_program = CP_FTREE;
173 else if (program_equal_continued(program, "LIFELINES")) {
174 compatibility_program = CP_LIFELINES;
176 else if (program_equal_continued(program, "PAF")) {
177 compatibility_program = CP_PAF;
179 else if (program_equal(program, "FamilyOrigins")) {
180 compatibility_program = CP_FAMORIG;
182 else if (program_equal(program, "EasyTree")) {
183 compatibility_program = CP_EASYTREE;
188 void compute_compatibility()
190 /* Reinitialize compatibility */
193 default_charset = "";
195 for (i = 0; i < C_NR_OF_RULES; i++)
198 switch (compatibility_program) {
200 if (compatibility_version >= 20000 && compatibility_version < 30000) {
201 compatibility = C_PAF2;
204 else if (compatibility_version >= 50000) {
205 compatibility = C_PAF5;
210 compatibility = data[compatibility_program].default_compat;
214 default_charset = data[compatibility_program].default_charset;
215 enable_compat_msg(data[compatibility_program].name, version);
219 void set_compatibility_version(const char* version)
221 if (compat_enabled) {
222 unsigned int major=0, minor=0, patch=0;
225 result = sscanf(version, " %u.%u.%u", &major, &minor, &patch);
227 gedcom_debug_print("Setting compat version to %u.%u.%u",
228 major, minor, patch);
229 compatibility_version = major * 10000 + minor * 100 + patch;
234 int compat_mode(Compat_rule rule)
236 return (compat_matrix[rule] & compatibility);
239 /********************************************************************/
241 /********************************************************************/
243 void compat_generate_submitter_link(Gedcom_ctxt parent)
245 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_USED,
247 struct tag_struct ts;
252 gedcom_warning(_("Adding link to submitter record with xref '%s'"),
254 self = start_element(ELT_HEAD_SUBM,
255 parent, 1, ts, SUBMITTER_LINK,
256 GEDCOM_MAKE_XREF_PTR(val1, xr));
257 end_element(ELT_HEAD_SUBM, parent, self, NULL);
258 compat_state[C_NO_SUBMITTER] = 1;
261 void compat_generate_submitter()
263 if (compat_state[C_NO_SUBMITTER]) {
264 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_DEFINED,
266 struct tag_struct ts;
267 Gedcom_ctxt self1, self2;
269 /* first generate "0 SUBM" */
272 self1 = start_record(REC_SUBM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
273 NULL, GEDCOM_MAKE_NULL(val2));
275 /* then generate "1 NAME ..." */
278 self2 = start_element(ELT_SUBM_NAME, self1, 1, ts, DEFAULT_SUBMITTER_NAME,
279 GEDCOM_MAKE_STRING(val1, DEFAULT_SUBMITTER_NAME));
281 /* close "1 NAME ..." */
282 end_element(ELT_SUBM_NAME, self1, self2, NULL);
285 end_record(REC_SUBM, self1, NULL);
286 compat_state[C_NO_SUBMITTER] = 0;
290 /********************************************************************/
292 /********************************************************************/
294 void compat_generate_gedcom(Gedcom_ctxt parent)
296 struct tag_struct ts;
297 Gedcom_ctxt self1, self2;
299 /* first generate "1 GEDC" */
302 self1 = start_element(ELT_HEAD_GEDC, parent, 1, ts, NULL,
303 GEDCOM_MAKE_NULL(val1));
305 /* then generate "2 VERS <DEFAULT_GEDC_VERS>" */
308 self2 = start_element(ELT_HEAD_GEDC_VERS, self1, 2, ts,
310 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_VERS));
313 end_element(ELT_HEAD_GEDC_VERS, self1, self2, NULL);
315 /* then generate "2 FORM <DEFAULT_GEDCOM_FORM> */
316 compat_generate_gedcom_form(self1);
319 end_element(ELT_HEAD_GEDC, parent, self1, NULL);
322 /********************************************************************/
324 /********************************************************************/
326 void compat_generate_gedcom_form(Gedcom_ctxt parent)
328 struct tag_struct ts;
331 /* generate "2 FORM <DEFAULT_GEDCOM_FORM> */
334 self = start_element(ELT_HEAD_GEDC_FORM, parent, 2, ts,
336 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
339 end_element(ELT_HEAD_GEDC_FORM, parent, self, NULL);
343 /********************************************************************/
345 /********************************************************************/
347 int compat_generate_char(Gedcom_ctxt parent)
349 struct tag_struct ts;
353 /* first generate "1 CHAR <DEFAULT_CHAR>" */
357 /* Must strdup, because default_charset is const char */
358 charset = strdup(default_charset);
362 self1 = start_element(ELT_HEAD_CHAR, parent, 1, ts, charset,
363 GEDCOM_MAKE_STRING(val1, charset));
367 end_element(ELT_HEAD_CHAR, parent, self1, NULL);
369 if (open_conv_to_internal(default_charset) == 0)
375 /********************************************************************/
377 /********************************************************************/
379 Gedcom_ctxt compat_generate_resi_start(Gedcom_ctxt parent)
382 struct tag_struct ts;
386 self = start_element(ELT_SUB_INDIV_RESI, parent, 1, ts, NULL,
387 GEDCOM_MAKE_NULL(val1));
391 void compat_generate_resi_end(Gedcom_ctxt parent, Gedcom_ctxt self)
393 end_element(ELT_SUB_INDIV_RESI, parent, self, NULL);
396 /********************************************************************/
398 /********************************************************************/
400 int is_551_tag(const char* tag)
402 if (strncmp(tag, "EMAIL", 6))
404 else if (strncmp(tag, "FONE", 5))
406 else if (strncmp(tag, "ROMN", 5))
412 int compat_check_551_tag(const char* tag, struct safe_buffer* b)
414 if (is_551_tag(tag)) {
416 SAFE_BUF_ADDCHAR(b, '_');
417 safe_buf_append(b, tag);
418 gedcom_warning(_("Converting 5.5.1 tag '%s' to standard 5.5 user tag '%s'"),
419 tag, get_buf_string(b));
426 /********************************************************************/
428 /********************************************************************/
430 void compat_generate_slgc_famc_link(Gedcom_ctxt parent)
432 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_USED,
434 struct tag_struct ts;
439 gedcom_warning(_("Adding link to family record with xref '%s'"),
441 self = start_element(ELT_SUB_LIO_SLGC_FAMC,
442 parent, 2, ts, SLGC_FAMC_LINK,
443 GEDCOM_MAKE_XREF_PTR(val1, xr));
444 end_element(ELT_SUB_LIO_SLGC_FAMC, parent, self, NULL);
445 compat_state[C_NO_SLGC_FAMC]++;
448 void compat_generate_slgc_famc_fam()
450 /* If bigger than 1, then the FAM record has already been generated */
451 if (compat_state[C_NO_SLGC_FAMC] == 1) {
452 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_DEFINED,
454 struct tag_struct ts;
457 /* generate "0 FAM" */
460 self = start_record(REC_FAM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
461 NULL, GEDCOM_MAKE_NULL(val2));
464 end_record(REC_FAM, self, NULL);
468 /********************************************************************/
470 /********************************************************************/
472 int compat_check_subm_comm(const char* tag, const char* parent_tag,
473 struct safe_buffer* b)
475 if (!strcmp(tag, "COMM") && !strcmp(parent_tag, "SUBM")) {
477 SAFE_BUF_ADDCHAR(b, '_');
478 safe_buf_append(b, tag);
479 gedcom_warning(_("Converting non-standard tag '%s' to user tag '%s'"),
480 tag, get_buf_string(b));
481 compat_state[C_SUBM_COMM] = 1;
488 void compat_close_subm_comm()
490 compat_state[C_SUBM_COMM] = 0;
493 int compat_check_subm_comm_cont(const char* tag)
495 if (compat_state[C_SUBM_COMM] && !strcmp(tag, "CONT")) {
496 compat_state[C_SUBM_COMM] = 2;
503 Gedcom_ctxt compat_subm_comm_cont_start(Gedcom_ctxt parent, char* str)
505 Gedcom_ctxt self = NULL;
506 struct tag_struct ts;
508 if (compat_state[C_SUBM_COMM] == 2) {
511 self = start_element(ELT_USER, parent, 2, ts, str, &val2);
517 void compat_subm_comm_cont_end(Gedcom_ctxt parent, Gedcom_ctxt self)
519 if (compat_state[C_SUBM_COMM] == 2) {
520 end_element(ELT_USER, parent, self, NULL);
521 compat_state[C_SUBM_COMM] = 1;