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"
44 enum _COMPAT_PROGRAM {
57 /* Incompatibility list (with GEDCOM 5.5):
60 - no submitter record, no submitter link in the header
61 - INDI.ADDR instead of INDI.RESI.ADDR
62 - NOTE doesn't have a value
65 - no submitter record, no submitter link in the header
66 - no GEDC field in the header
67 - no CHAR field in the header
68 - HEAD.TIME instead of HEAD.DATE.TIME (will be ignored here)
69 - '@' not written as '@@' in values
70 - lots of missing required values
72 - Personal Ancestral File 5:
73 - '@' not written as '@@' in values
74 - some 5.5.1 (draft) tags are used: EMAIL, FONE, ROMN
75 - no FAMC field in SLGC
77 - Personal Ancestral File 2:
78 - '@' not written as '@@' in values
79 - COMM tag in submitter record
80 - double dates written as e.g. '1815/1816' instead of '1815/16'
85 /* C_NO_SUBMITTER */ C_FTREE | C_LIFELINES | C_PAF2,
86 /* C_INDI_ADDR */ C_FTREE,
87 /* C_NOTE_NO_VALUE */ C_FTREE,
88 /* C_NO_GEDC */ C_LIFELINES | C_PAF2,
89 /* C_NO_CHAR */ C_LIFELINES,
90 /* C_HEAD_TIME */ C_LIFELINES,
91 /* C_NO_DOUBLE_AT */ C_LIFELINES | C_PAF5 | C_PAF2,
92 /* C_NO_REQUIRED_VALUES */ C_LIFELINES,
93 /* C_551_TAGS */ C_PAF5,
94 /* C_NO_SLGC_FAMC */ C_PAF5,
95 /* C_SUBM_COMM */ C_PAF2,
96 /* C_DOUBLE_DATES_4 */ C_PAF2
99 int compat_state[C_NR_OF_RULES];
101 /* Compatibility handling */
103 void gedcom_set_compat_handling(int enable_compat)
105 compat_enabled = enable_compat;
108 void enable_compat_msg(const char* program_name, int version)
111 gedcom_warning(_("Enabling compatibility with '%s', version %d"),
112 program_name, version);
114 gedcom_warning(_("Enabling compatibility with '%s'"),
118 void set_compatibility_program(const char* program)
120 if (compat_enabled) {
121 if (! strncmp(program, "ftree", 6)) {
122 compatibility_program = CP_FTREE;
124 else if (! strncmp(program, "LIFELINES", 9)) {
125 compatibility_program = CP_LIFELINES;
126 if (strlen(program) > 9)
127 set_compatibility_version(program + 9);
129 else if (! strncmp(program, "PAF", 3)) {
130 compatibility_program = CP_PAF;
131 if (strlen(program) > 3)
132 set_compatibility_version(program + 3);
137 void set_compatibility_version(const char* version)
139 if (compat_enabled) {
140 unsigned int major=0, minor=0, patch=0;
143 result = sscanf(version, " %u.%u.%u", &major, &minor, &patch);
145 gedcom_debug_print(_("Setting compat version to %u.%u.%u"),
146 major, minor, patch);
147 compatibility_version = major * 10000 + minor * 100 + patch;
152 void compute_compatibility()
154 /* Reinitialize compatibility */
156 default_charset = "";
158 for (i = 0; i < C_NR_OF_RULES; i++)
161 switch (compatibility_program) {
163 enable_compat_msg("ftree", 0);
164 compatibility = C_FTREE;
167 enable_compat_msg("Lifelines", 0);
168 compatibility = C_LIFELINES;
169 default_charset = "ANSI";
172 if (compatibility_version >= 20000 && compatibility_version < 30000) {
173 enable_compat_msg("Personal Ancestral File", 2);
174 compatibility = C_PAF2;
176 else if (compatibility_version >= 50000) {
177 enable_compat_msg("Personal Ancestral File", 5);
178 compatibility = C_PAF5;
186 int compat_mode(Compat_rule rule)
188 return (compat_matrix[rule] & compatibility);
191 /********************************************************************/
193 /********************************************************************/
195 void compat_generate_submitter_link(Gedcom_ctxt parent)
197 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_USED,
199 struct tag_struct ts;
204 gedcom_warning(_("Adding link to submitter record with xref '%s'"),
206 self = start_element(ELT_HEAD_SUBM,
207 parent, 1, ts, SUBMITTER_LINK,
208 GEDCOM_MAKE_XREF_PTR(val1, xr));
209 end_element(ELT_HEAD_SUBM, parent, self, NULL);
210 compat_state[C_NO_SUBMITTER] = 1;
213 void compat_generate_submitter()
215 if (compat_state[C_NO_SUBMITTER]) {
216 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_DEFINED,
218 struct tag_struct ts;
219 Gedcom_ctxt self1, self2;
221 /* first generate "0 SUBM" */
224 self1 = start_record(REC_SUBM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
225 NULL, GEDCOM_MAKE_NULL(val2));
227 /* then generate "1 NAME ..." */
230 self2 = start_element(ELT_SUBM_NAME, self1, 1, ts, DEFAULT_SUBMITTER_NAME,
231 GEDCOM_MAKE_STRING(val1, DEFAULT_SUBMITTER_NAME));
233 /* close "1 NAME ..." */
234 end_element(ELT_SUBM_NAME, self1, self2, NULL);
237 end_record(REC_SUBM, self1, NULL);
238 compat_state[C_NO_SUBMITTER] = 0;
242 /********************************************************************/
244 /********************************************************************/
246 void compat_generate_gedcom(Gedcom_ctxt parent)
248 struct tag_struct ts;
249 Gedcom_ctxt self1, self2;
251 /* first generate "1 GEDC" */
254 self1 = start_element(ELT_HEAD_GEDC, parent, 1, ts, NULL,
255 GEDCOM_MAKE_NULL(val1));
257 /* then generate "2 VERS <DEFAULT_GEDC_VERS>" */
260 self2 = start_element(ELT_HEAD_GEDC_VERS, self1, 2, ts,
262 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_VERS));
265 end_element(ELT_HEAD_GEDC_VERS, self1, self2, NULL);
267 /* then generate "2 FORM <DEFAULT_GEDCOM_FORM> */
270 self2 = start_element(ELT_HEAD_GEDC_FORM, self1, 2, ts,
272 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
275 end_element(ELT_HEAD_GEDC_FORM, self1, self2, NULL);
278 end_element(ELT_HEAD_GEDC, parent, self1, NULL);
281 /********************************************************************/
283 /********************************************************************/
285 int compat_generate_char(Gedcom_ctxt parent)
287 struct tag_struct ts;
291 /* first generate "1 CHAR <DEFAULT_CHAR>" */
295 /* Must strdup, because default_charset is const char */
296 charset = strdup(default_charset);
300 self1 = start_element(ELT_HEAD_CHAR, parent, 1, ts, charset,
301 GEDCOM_MAKE_STRING(val1, charset));
305 end_element(ELT_HEAD_CHAR, parent, self1, NULL);
307 if (open_conv_to_internal(default_charset) == 0)
313 /********************************************************************/
315 /********************************************************************/
317 Gedcom_ctxt compat_generate_resi_start(Gedcom_ctxt parent)
320 struct tag_struct ts;
324 self = start_element(ELT_SUB_INDIV_RESI, parent, 1, ts, NULL,
325 GEDCOM_MAKE_NULL(val1));
329 void compat_generate_resi_end(Gedcom_ctxt parent, Gedcom_ctxt self)
331 end_element(ELT_SUB_INDIV_RESI, parent, self, NULL);
334 /********************************************************************/
336 /********************************************************************/
338 int is_551_tag(const char* tag)
340 if (strncmp(tag, "EMAIL", 6))
342 else if (strncmp(tag, "FONE", 5))
344 else if (strncmp(tag, "ROMN", 5))
350 int compat_check_551_tag(const char* tag, struct safe_buffer* b)
352 if (is_551_tag(tag)) {
354 SAFE_BUF_ADDCHAR(b, '_');
355 safe_buf_append(b, tag);
356 gedcom_warning(_("Converting 5.5.1 tag '%s' to standard 5.5 user tag '%s'"),
357 tag, get_buf_string(b));
364 /********************************************************************/
366 /********************************************************************/
368 void compat_generate_slgc_famc_link(Gedcom_ctxt parent)
370 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_USED,
372 struct tag_struct ts;
377 gedcom_warning(_("Adding link to family record with xref '%s'"),
379 self = start_element(ELT_SUB_LIO_SLGC_FAMC,
380 parent, 2, ts, SLGC_FAMC_LINK,
381 GEDCOM_MAKE_XREF_PTR(val1, xr));
382 end_element(ELT_SUB_LIO_SLGC_FAMC, parent, self, NULL);
383 compat_state[C_NO_SLGC_FAMC]++;
386 void compat_generate_slgc_famc_fam()
388 /* If bigger than 1, then the FAM record has already been generated */
389 if (compat_state[C_NO_SLGC_FAMC] == 1) {
390 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_DEFINED,
392 struct tag_struct ts;
395 /* generate "0 FAM" */
398 self = start_record(REC_FAM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
399 NULL, GEDCOM_MAKE_NULL(val2));
402 end_record(REC_FAM, self, NULL);
406 /********************************************************************/
408 /********************************************************************/
410 int compat_check_subm_comm(const char* tag, const char* parent_tag,
411 struct safe_buffer* b)
413 if (!strcmp(tag, "COMM") && !strcmp(parent_tag, "SUBM")) {
415 SAFE_BUF_ADDCHAR(b, '_');
416 safe_buf_append(b, tag);
417 gedcom_warning(_("Converting non-standard tag '%s' to user tag '%s'"),
418 tag, get_buf_string(b));
419 compat_state[C_SUBM_COMM] = 1;
426 void compat_close_subm_comm()
428 compat_state[C_SUBM_COMM] = 0;
431 int compat_check_subm_comm_cont(const char* tag)
433 if (compat_state[C_SUBM_COMM] && !strcmp(tag, "CONT")) {
434 compat_state[C_SUBM_COMM] = 2;
441 Gedcom_ctxt compat_subm_comm_cont_start(Gedcom_ctxt parent, const char* str)
443 Gedcom_ctxt self = NULL;
444 struct tag_struct ts;
446 if (compat_state[C_SUBM_COMM] == 2) {
449 self = start_element(ELT_USER, parent, 2, ts, str, &val2);
455 void compat_subm_comm_cont_end(Gedcom_ctxt parent, Gedcom_ctxt self)
457 if (compat_state[C_SUBM_COMM] == 2) {
458 end_element(ELT_USER, parent, self, NULL);
459 compat_state[C_SUBM_COMM] = 1;