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 compatibility_program = 0;
121 if (compat_enabled) {
122 if (! strncmp(program, "ftree", 6)) {
123 compatibility_program = CP_FTREE;
125 else if (! strncmp(program, "LIFELINES", 9)) {
126 compatibility_program = CP_LIFELINES;
127 if (strlen(program) > 9)
128 set_compatibility_version(program + 9);
130 else if (! strncmp(program, "PAF", 3)) {
131 compatibility_program = CP_PAF;
132 if (strlen(program) > 3)
133 set_compatibility_version(program + 3);
138 void set_compatibility_version(const char* version)
140 if (compat_enabled) {
141 unsigned int major=0, minor=0, patch=0;
144 result = sscanf(version, " %u.%u.%u", &major, &minor, &patch);
146 gedcom_debug_print("Setting compat version to %u.%u.%u",
147 major, minor, patch);
148 compatibility_version = major * 10000 + minor * 100 + patch;
153 void compute_compatibility()
155 /* Reinitialize compatibility */
157 default_charset = "";
159 for (i = 0; i < C_NR_OF_RULES; i++)
162 switch (compatibility_program) {
164 enable_compat_msg("ftree", 0);
165 compatibility = C_FTREE;
168 enable_compat_msg("Lifelines", 0);
169 compatibility = C_LIFELINES;
170 default_charset = "ANSI";
173 if (compatibility_version >= 20000 && compatibility_version < 30000) {
174 enable_compat_msg("Personal Ancestral File", 2);
175 compatibility = C_PAF2;
177 else if (compatibility_version >= 50000) {
178 enable_compat_msg("Personal Ancestral File", 5);
179 compatibility = C_PAF5;
187 int compat_mode(Compat_rule rule)
189 return (compat_matrix[rule] & compatibility);
192 /********************************************************************/
194 /********************************************************************/
196 void compat_generate_submitter_link(Gedcom_ctxt parent)
198 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_USED,
200 struct tag_struct ts;
205 gedcom_warning(_("Adding link to submitter record with xref '%s'"),
207 self = start_element(ELT_HEAD_SUBM,
208 parent, 1, ts, SUBMITTER_LINK,
209 GEDCOM_MAKE_XREF_PTR(val1, xr));
210 end_element(ELT_HEAD_SUBM, parent, self, NULL);
211 compat_state[C_NO_SUBMITTER] = 1;
214 void compat_generate_submitter()
216 if (compat_state[C_NO_SUBMITTER]) {
217 struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_DEFINED,
219 struct tag_struct ts;
220 Gedcom_ctxt self1, self2;
222 /* first generate "0 SUBM" */
225 self1 = start_record(REC_SUBM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
226 NULL, GEDCOM_MAKE_NULL(val2));
228 /* then generate "1 NAME ..." */
231 self2 = start_element(ELT_SUBM_NAME, self1, 1, ts, DEFAULT_SUBMITTER_NAME,
232 GEDCOM_MAKE_STRING(val1, DEFAULT_SUBMITTER_NAME));
234 /* close "1 NAME ..." */
235 end_element(ELT_SUBM_NAME, self1, self2, NULL);
238 end_record(REC_SUBM, self1, NULL);
239 compat_state[C_NO_SUBMITTER] = 0;
243 /********************************************************************/
245 /********************************************************************/
247 void compat_generate_gedcom(Gedcom_ctxt parent)
249 struct tag_struct ts;
250 Gedcom_ctxt self1, self2;
252 /* first generate "1 GEDC" */
255 self1 = start_element(ELT_HEAD_GEDC, parent, 1, ts, NULL,
256 GEDCOM_MAKE_NULL(val1));
258 /* then generate "2 VERS <DEFAULT_GEDC_VERS>" */
261 self2 = start_element(ELT_HEAD_GEDC_VERS, self1, 2, ts,
263 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_VERS));
266 end_element(ELT_HEAD_GEDC_VERS, self1, self2, NULL);
268 /* then generate "2 FORM <DEFAULT_GEDCOM_FORM> */
271 self2 = start_element(ELT_HEAD_GEDC_FORM, self1, 2, ts,
273 GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
276 end_element(ELT_HEAD_GEDC_FORM, self1, self2, NULL);
279 end_element(ELT_HEAD_GEDC, parent, self1, NULL);
282 /********************************************************************/
284 /********************************************************************/
286 int compat_generate_char(Gedcom_ctxt parent)
288 struct tag_struct ts;
292 /* first generate "1 CHAR <DEFAULT_CHAR>" */
296 /* Must strdup, because default_charset is const char */
297 charset = strdup(default_charset);
301 self1 = start_element(ELT_HEAD_CHAR, parent, 1, ts, charset,
302 GEDCOM_MAKE_STRING(val1, charset));
306 end_element(ELT_HEAD_CHAR, parent, self1, NULL);
308 if (open_conv_to_internal(default_charset) == 0)
314 /********************************************************************/
316 /********************************************************************/
318 Gedcom_ctxt compat_generate_resi_start(Gedcom_ctxt parent)
321 struct tag_struct ts;
325 self = start_element(ELT_SUB_INDIV_RESI, parent, 1, ts, NULL,
326 GEDCOM_MAKE_NULL(val1));
330 void compat_generate_resi_end(Gedcom_ctxt parent, Gedcom_ctxt self)
332 end_element(ELT_SUB_INDIV_RESI, parent, self, NULL);
335 /********************************************************************/
337 /********************************************************************/
339 int is_551_tag(const char* tag)
341 if (strncmp(tag, "EMAIL", 6))
343 else if (strncmp(tag, "FONE", 5))
345 else if (strncmp(tag, "ROMN", 5))
351 int compat_check_551_tag(const char* tag, struct safe_buffer* b)
353 if (is_551_tag(tag)) {
355 SAFE_BUF_ADDCHAR(b, '_');
356 safe_buf_append(b, tag);
357 gedcom_warning(_("Converting 5.5.1 tag '%s' to standard 5.5 user tag '%s'"),
358 tag, get_buf_string(b));
365 /********************************************************************/
367 /********************************************************************/
369 void compat_generate_slgc_famc_link(Gedcom_ctxt parent)
371 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_USED,
373 struct tag_struct ts;
378 gedcom_warning(_("Adding link to family record with xref '%s'"),
380 self = start_element(ELT_SUB_LIO_SLGC_FAMC,
381 parent, 2, ts, SLGC_FAMC_LINK,
382 GEDCOM_MAKE_XREF_PTR(val1, xr));
383 end_element(ELT_SUB_LIO_SLGC_FAMC, parent, self, NULL);
384 compat_state[C_NO_SLGC_FAMC]++;
387 void compat_generate_slgc_famc_fam()
389 /* If bigger than 1, then the FAM record has already been generated */
390 if (compat_state[C_NO_SLGC_FAMC] == 1) {
391 struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_DEFINED,
393 struct tag_struct ts;
396 /* generate "0 FAM" */
399 self = start_record(REC_FAM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
400 NULL, GEDCOM_MAKE_NULL(val2));
403 end_record(REC_FAM, self, NULL);
407 /********************************************************************/
409 /********************************************************************/
411 int compat_check_subm_comm(const char* tag, const char* parent_tag,
412 struct safe_buffer* b)
414 if (!strcmp(tag, "COMM") && !strcmp(parent_tag, "SUBM")) {
416 SAFE_BUF_ADDCHAR(b, '_');
417 safe_buf_append(b, tag);
418 gedcom_warning(_("Converting non-standard tag '%s' to user tag '%s'"),
419 tag, get_buf_string(b));
420 compat_state[C_SUBM_COMM] = 1;
427 void compat_close_subm_comm()
429 compat_state[C_SUBM_COMM] = 0;
432 int compat_check_subm_comm_cont(const char* tag)
434 if (compat_state[C_SUBM_COMM] && !strcmp(tag, "CONT")) {
435 compat_state[C_SUBM_COMM] = 2;
442 Gedcom_ctxt compat_subm_comm_cont_start(Gedcom_ctxt parent, char* str)
444 Gedcom_ctxt self = NULL;
445 struct tag_struct ts;
447 if (compat_state[C_SUBM_COMM] == 2) {
450 self = start_element(ELT_USER, parent, 2, ts, str, &val2);
456 void compat_subm_comm_cont_end(Gedcom_ctxt parent, Gedcom_ctxt self)
458 if (compat_state[C_SUBM_COMM] == 2) {
459 end_element(ELT_USER, parent, self, NULL);
460 compat_state[C_SUBM_COMM] = 1;