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 {
51 const char* program_name[] = {
53 /* CP_FTREE */ "ftree",
54 /* CP_LIFELINES */ "Lifelines",
55 /* CP_PAF */ "Personal Ancestral File",
56 /* CP_FAMORIG */ "Family Origins"
67 /* Incompatibility list (with GEDCOM 5.5):
70 - no submitter record, no submitter link in the header
71 - INDI.ADDR instead of INDI.RESI.ADDR
72 - NOTE doesn't have a value
75 - no submitter record, no submitter link in the header
76 - no GEDC field in the header
77 - no CHAR field in the header
78 - HEAD.TIME instead of HEAD.DATE.TIME (will be ignored here)
79 - '@' not written as '@@' in values
80 - lots of missing required values
82 - Personal Ancestral File 5:
83 - '@' not written as '@@' in values
84 - some 5.5.1 (draft) tags are used: EMAIL, FONE, ROMN
85 - no FAMC field in SLGC
87 - Personal Ancestral File 2:
88 - '@' not written as '@@' in values
89 - COMM tag in submitter record
90 - double dates written as e.g. '1815/1816' instead of '1815/16'
93 - '@' not written as '@@' in values
98 /* C_NO_SUBMITTER */ C_FTREE | C_LIFELINES | C_PAF2,
99 /* C_INDI_ADDR */ C_FTREE,
100 /* C_NOTE_NO_VALUE */ C_FTREE,
101 /* C_NO_GEDC */ C_LIFELINES | C_PAF2,
102 /* C_NO_CHAR */ C_LIFELINES,
103 /* C_HEAD_TIME */ C_LIFELINES,
104 /* C_NO_DOUBLE_AT */ C_LIFELINES | C_PAF5 | C_PAF2 | C_FAMORIG,
105 /* C_NO_REQUIRED_VALUES */ C_LIFELINES,
106 /* C_551_TAGS */ C_PAF5,
107 /* C_NO_SLGC_FAMC */ C_PAF5,
108 /* C_SUBM_COMM */ C_PAF2,
109 /* C_DOUBLE_DATES_4 */ C_PAF2
112 int compat_state[C_NR_OF_RULES];
114 /* Compatibility handling */
116 void gedcom_set_compat_handling(int enable_compat)
118 compat_enabled = enable_compat;
121 void enable_compat_msg(const char* program_name, int version)
124 gedcom_warning(_("Enabling compatibility with '%s', version %d"),
125 program_name, version);
127 gedcom_warning(_("Enabling compatibility with '%s'"),
131 int program_equal(const char* program, const char* compare)
133 return !strncmp(program, compare, strlen(compare)+1);
136 int program_equal_continued(const char* program, const char* compare)
138 size_t len = strlen(compare);
139 int result = strncmp(program, compare, len);
141 if (strlen(program) > len)
142 set_compatibility_version(program + len);
147 void set_compatibility_program(const char* program)
149 compatibility_program = 0;
150 if (compat_enabled) {
151 if (program_equal(program, "ftree")) {
152 compatibility_program = CP_FTREE;
154 else if (program_equal_continued(program, "LIFELINES")) {
155 compatibility_program = CP_LIFELINES;
157 else if (program_equal_continued(program, "PAF")) {
158 compatibility_program = CP_PAF;
160 else if (program_equal(program, "FamilyOrigins")) {
161 compatibility_program = CP_FAMORIG;
166 void compute_compatibility()
168 /* Reinitialize compatibility */
171 default_charset = "";
173 for (i = 0; i < C_NR_OF_RULES; i++)
176 switch (compatibility_program) {
178 compatibility = C_FTREE;
181 compatibility = C_LIFELINES;
182 default_charset = "ANSI";
185 if (compatibility_version >= 20000 && compatibility_version < 30000) {
186 compatibility = C_PAF2;
189 else if (compatibility_version >= 50000) {
190 compatibility = C_PAF5;
195 compatibility = C_FAMORIG;
201 enable_compat_msg(program_name[compatibility_program], version);
204 void set_compatibility_version(const char* version)
206 if (compat_enabled) {
207 unsigned int major=0, minor=0, patch=0;
210 result = sscanf(version, " %u.%u.%u", &major, &minor, &patch);
212 gedcom_debug_print("Setting compat version to %u.%u.%u",
213 major, minor, patch);
214 compatibility_version = major * 10000 + minor * 100 + patch;
219 int compat_mode(Compat_rule rule)
221 return (compat_matrix[rule] & compatibility);
224 /********************************************************************/
226 /********************************************************************/
228 void compat_generate_submitter_link(Gedcom_ctxt parent)
230 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_USED,
232 struct tag_struct ts;
237 gedcom_warning(_("Adding link to submitter record with xref '%s'"),
239 self = start_element(ELT_HEAD_SUBM,
240 parent, 1, ts, SUBMITTER_LINK,
241 GEDCOM_MAKE_XREF_PTR(val1, xr));
242 end_element(ELT_HEAD_SUBM, parent, self, NULL);
243 compat_state[C_NO_SUBMITTER] = 1;
246 void compat_generate_submitter()
248 if (compat_state[C_NO_SUBMITTER]) {
249 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_DEFINED,
251 struct tag_struct ts;
252 Gedcom_ctxt self1, self2;
254 /* first generate "0 SUBM" */
257 self1 = start_record(REC_SUBM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
258 NULL, GEDCOM_MAKE_NULL(val2));
260 /* then generate "1 NAME ..." */
263 self2 = start_element(ELT_SUBM_NAME, self1, 1, ts, DEFAULT_SUBMITTER_NAME,
264 GEDCOM_MAKE_STRING(val1, DEFAULT_SUBMITTER_NAME));
266 /* close "1 NAME ..." */
267 end_element(ELT_SUBM_NAME, self1, self2, NULL);
270 end_record(REC_SUBM, self1, NULL);
271 compat_state[C_NO_SUBMITTER] = 0;
275 /********************************************************************/
277 /********************************************************************/
279 void compat_generate_gedcom(Gedcom_ctxt parent)
281 struct tag_struct ts;
282 Gedcom_ctxt self1, self2;
284 /* first generate "1 GEDC" */
287 self1 = start_element(ELT_HEAD_GEDC, parent, 1, ts, NULL,
288 GEDCOM_MAKE_NULL(val1));
290 /* then generate "2 VERS <DEFAULT_GEDC_VERS>" */
293 self2 = start_element(ELT_HEAD_GEDC_VERS, self1, 2, ts,
295 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_VERS));
298 end_element(ELT_HEAD_GEDC_VERS, self1, self2, NULL);
300 /* then generate "2 FORM <DEFAULT_GEDCOM_FORM> */
303 self2 = start_element(ELT_HEAD_GEDC_FORM, self1, 2, ts,
305 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
308 end_element(ELT_HEAD_GEDC_FORM, self1, self2, NULL);
311 end_element(ELT_HEAD_GEDC, parent, self1, NULL);
314 /********************************************************************/
316 /********************************************************************/
318 int compat_generate_char(Gedcom_ctxt parent)
320 struct tag_struct ts;
324 /* first generate "1 CHAR <DEFAULT_CHAR>" */
328 /* Must strdup, because default_charset is const char */
329 charset = strdup(default_charset);
333 self1 = start_element(ELT_HEAD_CHAR, parent, 1, ts, charset,
334 GEDCOM_MAKE_STRING(val1, charset));
338 end_element(ELT_HEAD_CHAR, parent, self1, NULL);
340 if (open_conv_to_internal(default_charset) == 0)
346 /********************************************************************/
348 /********************************************************************/
350 Gedcom_ctxt compat_generate_resi_start(Gedcom_ctxt parent)
353 struct tag_struct ts;
357 self = start_element(ELT_SUB_INDIV_RESI, parent, 1, ts, NULL,
358 GEDCOM_MAKE_NULL(val1));
362 void compat_generate_resi_end(Gedcom_ctxt parent, Gedcom_ctxt self)
364 end_element(ELT_SUB_INDIV_RESI, parent, self, NULL);
367 /********************************************************************/
369 /********************************************************************/
371 int is_551_tag(const char* tag)
373 if (strncmp(tag, "EMAIL", 6))
375 else if (strncmp(tag, "FONE", 5))
377 else if (strncmp(tag, "ROMN", 5))
383 int compat_check_551_tag(const char* tag, struct safe_buffer* b)
385 if (is_551_tag(tag)) {
387 SAFE_BUF_ADDCHAR(b, '_');
388 safe_buf_append(b, tag);
389 gedcom_warning(_("Converting 5.5.1 tag '%s' to standard 5.5 user tag '%s'"),
390 tag, get_buf_string(b));
397 /********************************************************************/
399 /********************************************************************/
401 void compat_generate_slgc_famc_link(Gedcom_ctxt parent)
403 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_USED,
405 struct tag_struct ts;
410 gedcom_warning(_("Adding link to family record with xref '%s'"),
412 self = start_element(ELT_SUB_LIO_SLGC_FAMC,
413 parent, 2, ts, SLGC_FAMC_LINK,
414 GEDCOM_MAKE_XREF_PTR(val1, xr));
415 end_element(ELT_SUB_LIO_SLGC_FAMC, parent, self, NULL);
416 compat_state[C_NO_SLGC_FAMC]++;
419 void compat_generate_slgc_famc_fam()
421 /* If bigger than 1, then the FAM record has already been generated */
422 if (compat_state[C_NO_SLGC_FAMC] == 1) {
423 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_DEFINED,
425 struct tag_struct ts;
428 /* generate "0 FAM" */
431 self = start_record(REC_FAM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
432 NULL, GEDCOM_MAKE_NULL(val2));
435 end_record(REC_FAM, self, NULL);
439 /********************************************************************/
441 /********************************************************************/
443 int compat_check_subm_comm(const char* tag, const char* parent_tag,
444 struct safe_buffer* b)
446 if (!strcmp(tag, "COMM") && !strcmp(parent_tag, "SUBM")) {
448 SAFE_BUF_ADDCHAR(b, '_');
449 safe_buf_append(b, tag);
450 gedcom_warning(_("Converting non-standard tag '%s' to user tag '%s'"),
451 tag, get_buf_string(b));
452 compat_state[C_SUBM_COMM] = 1;
459 void compat_close_subm_comm()
461 compat_state[C_SUBM_COMM] = 0;
464 int compat_check_subm_comm_cont(const char* tag)
466 if (compat_state[C_SUBM_COMM] && !strcmp(tag, "CONT")) {
467 compat_state[C_SUBM_COMM] = 2;
474 Gedcom_ctxt compat_subm_comm_cont_start(Gedcom_ctxt parent, char* str)
476 Gedcom_ctxt self = NULL;
477 struct tag_struct ts;
479 if (compat_state[C_SUBM_COMM] == 2) {
482 self = start_element(ELT_USER, parent, 2, ts, str, &val2);
488 void compat_subm_comm_cont_end(Gedcom_ctxt parent, Gedcom_ctxt self)
490 if (compat_state[C_SUBM_COMM] == 2) {
491 end_element(ELT_USER, parent, self, NULL);
492 compat_state[C_SUBM_COMM] = 1;