Small fixes.
[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 };
82
83 int compat_state[C_NR_OF_RULES];
84
85 /* Compatibility handling */
86
87 void gedcom_set_compat_handling(int enable_compat)
88 {
89   compat_enabled = enable_compat;
90 }
91
92 void enable_compat_msg(const char* program_name)
93 {
94   gedcom_warning(_("Enabling compatibility with '%s'"), program_name);
95 }
96
97 void set_compatibility(const char* program)
98 {
99   /* Reinitialize compatibility */
100   int i;
101   default_charset = "";
102   compatibility = 0;
103   for (i = 0; i < C_NR_OF_RULES; i++)
104     compat_state[i] = 0;
105   
106   if (compat_enabled) {
107     if (! strncmp(program, "ftree", 6)) {
108       enable_compat_msg("ftree");
109       compatibility = C_FTREE;
110     }
111     else if (! strncmp(program, "LIFELINES", 9)) {
112       /* Matches "LIFELINES 3.0.2" */
113       enable_compat_msg("Lifelines");
114       compatibility = C_LIFELINES;
115       default_charset = "ANSI";
116     }
117     else if (! strncmp(program, "PAF", 4)) {
118       enable_compat_msg("Personal Ancestral File");
119       compatibility = C_PAF;
120     }
121   }
122 }
123
124 int compat_mode(Compat_rule rule)
125 {
126   return (compat_matrix[rule] & compatibility);
127 }
128
129 void compat_generate_submitter_link(Gedcom_ctxt parent)
130 {
131   struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_USED,
132                                             XREF_SUBM);
133   struct tag_struct ts;
134   Gedcom_ctxt self;
135   
136   ts.string = "SUBM";
137   ts.value  = TAG_SUBM;
138   gedcom_warning(_("Adding link to submitter record with xref '%s'"),
139                  SUBMITTER_LINK);
140   self = start_element(ELT_HEAD_SUBM,
141                        parent, 1, ts, SUBMITTER_LINK,
142                        GEDCOM_MAKE_XREF_PTR(val1, xr));
143   end_element(ELT_HEAD_SUBM, parent, self, NULL);
144   compat_state[C_NO_SUBMITTER] = 1;
145 }
146
147 void compat_generate_submitter()
148 {
149   if (compat_state[C_NO_SUBMITTER]) {
150     struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_DEFINED,
151                                               XREF_SUBM);
152     struct tag_struct ts;
153     Gedcom_ctxt self1, self2;
154     
155     /* first generate "0 SUBM" */
156     ts.string = "SUBM";
157     ts.value  = TAG_SUBM;
158     self1 = start_record(REC_SUBM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
159                          NULL, GEDCOM_MAKE_NULL(val2));
160     
161     /* then generate "1 NAME ..." */
162     ts.string = "NAME";
163     ts.value  = TAG_NAME;
164     self2 = start_element(ELT_SUBM_NAME, self1, 1, ts, DEFAULT_SUBMITTER_NAME,
165                           GEDCOM_MAKE_STRING(val1, DEFAULT_SUBMITTER_NAME));
166     
167     /* close "1 NAME ..." */
168     end_element(ELT_SUBM_NAME, self1, self2, NULL);
169     
170     /* close "0 SUBM" */
171     end_record(REC_SUBM, self1, NULL);
172     compat_state[C_NO_SUBMITTER] = 0;
173   }
174 }
175
176 void compat_generate_gedcom(Gedcom_ctxt parent)
177 {
178   struct tag_struct ts;
179   Gedcom_ctxt self1, self2;
180   
181   /* first generate "1 GEDC" */
182   ts.string = "GEDC";
183   ts.value  = TAG_GEDC;
184   self1 = start_element(ELT_HEAD_GEDC, parent, 1, ts, NULL,
185                         GEDCOM_MAKE_NULL(val1));
186   
187   /* then generate "2 VERS <DEFAULT_GEDC_VERS>" */
188   ts.string = "VERS";
189   ts.value  = TAG_VERS;
190   self2 = start_element(ELT_HEAD_GEDC_VERS, self1, 2, ts,
191                         DEFAULT_GEDCOM_VERS,
192                         GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_VERS));
193   
194   /* close "2 VERS" */
195   end_element(ELT_HEAD_GEDC_VERS, self1, self2, NULL);
196   
197   /* then generate "2 FORM <DEFAULT_GEDCOM_FORM> */
198   ts.string = "FORM";
199   ts.value  = TAG_FORM;
200   self2 = start_element(ELT_HEAD_GEDC_FORM, self1, 2, ts,
201                         DEFAULT_GEDCOM_FORM,
202                         GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
203   
204   /* close "2 FORM" */
205   end_element(ELT_HEAD_GEDC_FORM, self1, self2, NULL);
206   
207   /* close "1 GEDC" */
208   end_element(ELT_HEAD_GEDC, parent, self1, NULL);
209 }
210
211 int compat_generate_char(Gedcom_ctxt parent)
212 {
213   struct tag_struct ts;
214   Gedcom_ctxt self1;
215   char* charset;
216   
217   /* first generate "1 CHAR <DEFAULT_CHAR>" */
218   ts.string = "CHAR";
219   ts.value  = TAG_CHAR;
220
221   /* Must strdup, because default_charset is const char */
222   charset   = strdup(default_charset);
223   if (! charset)
224     MEMORY_ERROR;
225   else {
226     self1 = start_element(ELT_HEAD_CHAR, parent, 1, ts, charset,
227                           GEDCOM_MAKE_STRING(val1, charset));
228     free(charset);
229     
230     /* close "1 CHAR" */
231     end_element(ELT_HEAD_CHAR, parent, self1, NULL);
232   }
233   if (open_conv_to_internal(default_charset) == 0)
234     return 1;
235   else
236     return 0;
237 }
238
239 Gedcom_ctxt compat_generate_resi_start(Gedcom_ctxt parent)
240 {
241   Gedcom_ctxt self;
242   struct tag_struct ts;
243
244   ts.string = "RESI";
245   ts.value  = TAG_RESI;
246   self = start_element(ELT_SUB_INDIV_RESI, parent, 1, ts, NULL,
247                        GEDCOM_MAKE_NULL(val1));
248   return self;
249 }
250
251 void compat_generate_resi_end(Gedcom_ctxt parent, Gedcom_ctxt self)
252 {
253   end_element(ELT_SUB_INDIV_RESI, parent, self, NULL);
254 }
255
256 int is_551_tag(const char* tag)
257 {
258   if (strncmp(tag, "EMAIL", 6))
259     return 1;
260   else if (strncmp(tag, "FONE", 5))
261     return 1;
262   else if (strncmp(tag, "ROMN", 5))
263     return 1;
264   else
265     return 0;
266 }
267
268 int compat_check_551_tag(const char* tag, struct safe_buffer* b)
269 {
270   if (is_551_tag(tag)) {
271     reset_buffer(b);
272     SAFE_BUF_ADDCHAR(b, '_');
273     safe_buf_append(b, tag);
274     gedcom_warning(_("Converting 5.5.1 tag '%s' to standard 5.5 user tag '%s'"),
275                    tag, get_buf_string(b));
276     return 1;
277   }
278   else
279     return 0;
280 }
281
282 void compat_generate_slgc_famc_link(Gedcom_ctxt parent)
283 {
284   struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_USED,
285                                             XREF_FAM);
286   struct tag_struct ts;
287   Gedcom_ctxt self;
288   
289   ts.string = "FAMC";
290   ts.value  = TAG_FAMC;
291   gedcom_warning(_("Adding link to family record with xref '%s'"),
292                  SLGC_FAMC_LINK);
293   self = start_element(ELT_SUB_LIO_SLGC_FAMC,
294                        parent, 2, ts, SLGC_FAMC_LINK,
295                        GEDCOM_MAKE_XREF_PTR(val1, xr));
296   end_element(ELT_SUB_LIO_SLGC_FAMC, parent, self, NULL);
297   compat_state[C_NO_SLGC_FAMC]++;
298 }
299
300 void compat_generate_slgc_famc_fam()
301 {
302   /* If bigger than 1, then the FAM record has already been generated */
303   if (compat_state[C_NO_SLGC_FAMC] == 1) {
304     struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_DEFINED,
305                                               XREF_FAM);
306     struct tag_struct ts;
307     Gedcom_ctxt self;
308     
309     /* generate "0 FAM" */
310     ts.string = "FAM";
311     ts.value  = TAG_FAM;
312     self = start_record(REC_FAM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
313                         NULL, GEDCOM_MAKE_NULL(val2));
314     
315     /* close "0 FAM" */
316     end_record(REC_FAM, self, NULL);
317   }
318 }