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