Compatibility with Personal Ancestral File.
[gedcom-parse.git] / gedcom / compat.c
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.
5
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.
10
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.
15
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
19    02111-1307 USA.  */
20
21 /* $Id$ */
22 /* $Name$ */
23
24 #include "compat.h"
25 #include "interface.h"
26 #include "encoding.h"
27 #include "xref.h"
28 #include "buffer.h"
29 #include "gedcom_internal.h"
30 #include "gedcom.h"
31
32 int compat_enabled = 1;
33 int compatibility  = 0; 
34 const char* default_charset = "";
35
36 #define SUBMITTER_LINK         "@__COMPAT__SUBM__@"
37 #define SLGC_FAMC_LINK         "@__COMPAT__FAM_SLGC__@"
38 #define DEFAULT_SUBMITTER_NAME "Submitter"
39 #define DEFAULT_GEDCOM_VERS    "5.5"
40 #define DEFAULT_GEDCOM_FORM    "LINEAGE-LINKED"
41
42 enum _COMPAT {
43   C_FTREE = 0x01,
44   C_LIFELINES = 0x02,
45   C_PAF = 0x04
46 };
47
48 /* Incompatibility list (with GEDCOM 5.5):
49
50     - ftree:
51         - no submitter record, no submitter link in the header
52         - INDI.ADDR instead of INDI.RESI.ADDR
53         - NOTE doesn't have a value
54
55     - Lifelines (3.0.2):
56         - no submitter record, no submitter link in the header
57         - no GEDC field in the header
58         - no CHAR field in the header
59         - HEAD.TIME instead of HEAD.DATE.TIME (will be ignored here)
60         - '@' not written as '@@' in values
61         - lots of missing required values
62
63     - Personal Ancestral File:
64         - '@' not written as '@@' in values
65         - some 5.5.1 (draft) tags are used: EMAIL, FONE, ROMN
66         - no FAMC field in SLGC
67  */
68
69 int compat_matrix[] =
70 {
71   /* C_NO_SUBMITTER */        C_FTREE | C_LIFELINES,
72   /* C_INDI_ADDR */           C_FTREE,
73   /* C_NOTE_NO_VALUE */       C_FTREE,
74   /* C_NO_GEDC */             C_LIFELINES,
75   /* C_NO_CHAR */             C_LIFELINES,
76   /* C_HEAD_TIME */           C_LIFELINES,
77   /* C_NO_DOUBLE_AT */        C_LIFELINES | C_PAF,
78   /* C_NO_REQUIRED_VALUES */  C_LIFELINES,
79   /* C_551_TAGS */            C_PAF,
80   /* C_NO_SLGC_FAMC */        C_PAF,
81   /* C_NR_OF_RULES */         0
82 };
83
84 int compat_state[] =
85 {
86   /* C_NO_SUBMITTER */        0,
87   /* C_INDI_ADDR */           0,
88   /* C_NOTE_NO_VALUE */       0,
89   /* C_NO_GEDC */             0,
90   /* C_NO_CHAR */             0,
91   /* C_HEAD_TIME */           0,
92   /* C_NO_DOUBLE_AT */        0,
93   /* C_NO_REQUIRED_VALUES */  0,
94   /* C_551_TAGS */            0,
95   /* C_NO_SLGC_FAMC */        0,
96   /* C_NR_OF_RULES */         0
97 };
98
99 /* Compatibility handling */
100
101 void gedcom_set_compat_handling(int enable_compat)
102 {
103   compat_enabled = enable_compat;
104 }
105
106 void enable_compat_msg(const char* program_name)
107 {
108   gedcom_warning(_("Enabling compatibility with '%s'"), program_name);
109 }
110
111 void set_compatibility(const char* program)
112 {
113   /* Reinitialize compatibility */
114   int i;
115   default_charset = "";
116   compatibility = 0;
117   for (i = 0; i < C_NR_OF_RULES; i++)
118     compat_state[i] = 0;
119   
120   if (compat_enabled) {
121     if (! strncmp(program, "ftree", 6)) {
122       enable_compat_msg("ftree");
123       compatibility = C_FTREE;
124     }
125     else if (! strncmp(program, "LIFELINES", 9)) {
126       /* Matches "LIFELINES 3.0.2" */
127       enable_compat_msg("Lifelines");
128       compatibility = C_LIFELINES;
129       default_charset = "ANSI";
130     }
131     else if (! strncmp(program, "PAF", 4)) {
132       enable_compat_msg("Personal Ancestral File");
133       compatibility = C_PAF;
134     }
135   }
136 }
137
138 int compat_mode(Compat_rule rule)
139 {
140   return (compat_matrix[rule] & compatibility);
141 }
142
143 void compat_generate_submitter_link(Gedcom_ctxt parent)
144 {
145   struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_USED,
146                                             XREF_SUBM);
147   struct tag_struct ts;
148   Gedcom_ctxt self;
149   
150   ts.string = "SUBM";
151   ts.value  = TAG_SUBM;
152   gedcom_warning(_("Adding link to submitter record with xref '%s'"),
153                  SUBMITTER_LINK);
154   self = start_element(ELT_HEAD_SUBM,
155                        parent, 1, ts, SUBMITTER_LINK,
156                        GEDCOM_MAKE_XREF_PTR(val1, xr));
157   end_element(ELT_HEAD_SUBM, parent, self, NULL);
158   compat_state[C_NO_SUBMITTER] = 1;
159 }
160
161 void compat_generate_submitter()
162 {
163   if (compat_state[C_NO_SUBMITTER]) {
164     struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_DEFINED,
165                                               XREF_SUBM);
166     struct tag_struct ts;
167     Gedcom_ctxt self1, self2;
168     
169     /* first generate "0 SUBM" */
170     ts.string = "SUBM";
171     ts.value  = TAG_SUBM;
172     self1 = start_record(REC_SUBM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
173                          NULL, GEDCOM_MAKE_NULL(val2));
174     
175     /* then generate "1 NAME ..." */
176     ts.string = "NAME";
177     ts.value  = TAG_NAME;
178     self2 = start_element(ELT_SUBM_NAME, self1, 1, ts, DEFAULT_SUBMITTER_NAME,
179                           GEDCOM_MAKE_STRING(val1, DEFAULT_SUBMITTER_NAME));
180     
181     /* close "1 NAME ..." */
182     end_element(ELT_SUBM_NAME, self1, self2, NULL);
183     
184     /* close "0 SUBM" */
185     end_record(REC_SUBM, self1, NULL);
186     compat_state[C_NO_SUBMITTER] = 0;
187   }
188 }
189
190 void compat_generate_gedcom(Gedcom_ctxt parent)
191 {
192   struct tag_struct ts;
193   Gedcom_ctxt self1, self2;
194   
195   /* first generate "1 GEDC" */
196   ts.string = "GEDC";
197   ts.value  = TAG_GEDC;
198   self1 = start_element(ELT_HEAD_GEDC, parent, 1, ts, NULL,
199                         GEDCOM_MAKE_NULL(val1));
200   
201   /* then generate "2 VERS <DEFAULT_GEDC_VERS>" */
202   ts.string = "VERS";
203   ts.value  = TAG_VERS;
204   self2 = start_element(ELT_HEAD_GEDC_VERS, self1, 2, ts,
205                         DEFAULT_GEDCOM_VERS,
206                         GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_VERS));
207   
208   /* close "2 VERS" */
209   end_element(ELT_HEAD_GEDC_VERS, self1, self2, NULL);
210   
211   /* then generate "2 FORM <DEFAULT_GEDCOM_FORM> */
212   ts.string = "FORM";
213   ts.value  = TAG_FORM;
214   self2 = start_element(ELT_HEAD_GEDC_FORM, self1, 2, ts,
215                         DEFAULT_GEDCOM_FORM,
216                         GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
217   
218   /* close "2 FORM" */
219   end_element(ELT_HEAD_GEDC_FORM, self1, self2, NULL);
220   
221   /* close "1 GEDC" */
222   end_element(ELT_HEAD_GEDC, parent, self1, NULL);
223 }
224
225 int compat_generate_char(Gedcom_ctxt parent)
226 {
227   struct tag_struct ts;
228   Gedcom_ctxt self1;
229   char* charset;
230   
231   /* first generate "1 CHAR <DEFAULT_CHAR>" */
232   ts.string = "CHAR";
233   ts.value  = TAG_CHAR;
234
235   /* Must strdup, because default_charset is const char */
236   charset   = strdup(default_charset);
237   if (! charset)
238     MEMORY_ERROR;
239   else {
240     self1 = start_element(ELT_HEAD_CHAR, parent, 1, ts, charset,
241                           GEDCOM_MAKE_STRING(val1, charset));
242     free(charset);
243     
244     /* close "1 CHAR" */
245     end_element(ELT_HEAD_CHAR, parent, self1, NULL);
246   }
247   if (open_conv_to_internal(default_charset) == 0)
248     return 1;
249   else
250     return 0;
251 }
252
253 Gedcom_ctxt compat_generate_resi_start(Gedcom_ctxt parent)
254 {
255   Gedcom_ctxt self;
256   struct tag_struct ts;
257
258   ts.string = "RESI";
259   ts.value  = TAG_RESI;
260   self = start_element(ELT_SUB_INDIV_RESI, parent, 1, ts, NULL,
261                        GEDCOM_MAKE_NULL(val1));
262   return self;
263 }
264
265 void compat_generate_resi_end(Gedcom_ctxt parent, Gedcom_ctxt self)
266 {
267   end_element(ELT_SUB_INDIV_RESI, parent, self, NULL);
268 }
269
270 int is_551_tag(const char* tag)
271 {
272   if (strncmp(tag, "EMAIL", 6))
273     return 1;
274   else if (strncmp(tag, "FONE", 5))
275     return 1;
276   else if (strncmp(tag, "ROMN", 5))
277     return 1;
278   else
279     return 0;
280 }
281
282 int compat_check_551_tag(const char* tag, struct safe_buffer* b)
283 {
284   if (is_551_tag(tag)) {
285     reset_buffer(b);
286     SAFE_BUF_ADDCHAR(b, '_');
287     safe_buf_append(b, tag);
288     gedcom_warning(_("Converting 5.5.1 tag '%s' to standard 5.5 user tag '%s'"),
289                    tag, get_buf_string(b));
290     return 1;
291   }
292   else
293     return 0;
294 }
295
296 void compat_generate_slgc_famc_link(Gedcom_ctxt parent)
297 {
298   struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_USED,
299                                             XREF_FAM);
300   struct tag_struct ts;
301   Gedcom_ctxt self;
302   
303   ts.string = "FAMC";
304   ts.value  = TAG_FAMC;
305   gedcom_warning(_("Adding link to family record with xref '%s'"),
306                  SLGC_FAMC_LINK);
307   self = start_element(ELT_SUB_LIO_SLGC_FAMC,
308                        parent, 2, ts, SLGC_FAMC_LINK,
309                        GEDCOM_MAKE_XREF_PTR(val1, xr));
310   end_element(ELT_SUB_LIO_SLGC_FAMC, parent, self, NULL);
311   compat_state[C_NO_SLGC_FAMC]++;
312 }
313
314 void compat_generate_slgc_famc_fam()
315 {
316   /* If bigger than 1, then the FAM record has already been generated */
317   if (compat_state[C_NO_SLGC_FAMC] == 1) {
318     struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_DEFINED,
319                                               XREF_FAM);
320     struct tag_struct ts;
321     Gedcom_ctxt self;
322     
323     /* generate "0 FAM" */
324     ts.string = "FAM";
325     ts.value  = TAG_FAM;
326     self = start_record(REC_FAM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
327                         NULL, GEDCOM_MAKE_NULL(val2));
328     
329     /* close "0 FAM" */
330     end_record(REC_FAM, self, NULL);
331   }
332 }