Recognize the UTF-8 byte order mark.
[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 "gedcom_internal.h"
29 #include "gedcom.h"
30
31 int compat_enabled = 1;
32 int compatibility  = 0; 
33 const char* default_charset = "";
34
35 #define SUBMITTER_LINK         "@__COMPAT__SUBM__@"
36 #define DEFAULT_SUBMITTER_NAME "Submitter"
37 #define DEFAULT_GEDCOM_VERS    "5.5"
38 #define DEFAULT_GEDCOM_FORM    "LINEAGE-LINKED"
39
40 enum _COMPAT {
41   C_FTREE = 0x01,
42   C_LIFELINES = 0x02
43 };
44
45 /* Incompatibility list (with GEDCOM 5.5):
46
47     - ftree:
48         - no submitter record, no submitter link in the header
49         - INDI.ADDR instead of INDI.RESI.ADDR
50         - NOTE doesn't have a value
51
52     - Lifelines (3.0.2):
53         - no submitter record, no submitter link in the header
54         - no GEDC field in the header
55         - no CHAR field in the header
56         - HEAD.TIME instead of HEAD.DATE.TIME (will be ignored here)
57         - '@' not written as '@@' in values
58         - lots of missing required values
59  */
60
61 int compat_matrix[] =
62 {
63   /* C_NO_SUBMITTER */        C_FTREE | C_LIFELINES,
64   /* C_INDI_ADDR */           C_FTREE,
65   /* C_NOTE_NO_VALUE */       C_FTREE,
66   /* C_NO_GEDC */             C_LIFELINES,
67   /* C_NO_CHAR */             C_LIFELINES,
68   /* C_HEAD_TIME */           C_LIFELINES,
69   /* C_NO_DOUBLE_AT */        C_LIFELINES,
70   /* C_NO_REQUIRED_VALUES */  C_LIFELINES
71 };
72
73 /* Compatibility handling */
74
75 void gedcom_set_compat_handling(int enable_compat)
76 {
77   compat_enabled = enable_compat;
78 }
79
80 void set_compatibility(const char* program)
81 {
82   /* Reinitialize compatibility */
83   default_charset = "";
84   compatibility = 0;
85   
86   if (compat_enabled) {
87     if (! strncmp(program, "ftree", 6)) {
88       gedcom_warning(_("Enabling compatibility with 'ftree'"));
89       compatibility = C_FTREE;
90     }
91     else if (! strncmp(program, "LIFELINES", 9)) {
92       /* Matches "LIFELINES 3.0.2" */
93       gedcom_warning(_("Enabling compatibility with 'Lifelines'"));
94       compatibility = C_LIFELINES;
95       default_charset = "ANSI";
96     }
97   }
98 }
99
100 int compat_mode(Compat_rule rule)
101 {
102   return (compat_matrix[rule] & compatibility);
103 }
104
105 void compat_generate_submitter_link(Gedcom_ctxt parent)
106 {
107   struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_USED,
108                                             XREF_SUBM);
109   struct tag_struct ts;
110   Gedcom_ctxt self;
111   
112   ts.string = "SUBM";
113   ts.value  = TAG_SUBM;
114   self = start_element(ELT_HEAD_SUBM,
115                        parent, 1, ts, SUBMITTER_LINK,
116                        GEDCOM_MAKE_XREF_PTR(val1, xr));
117   end_element(ELT_HEAD_SUBM, parent, self, NULL);
118 }
119
120 void compat_generate_submitter()
121 {
122   struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_DEFINED,
123                                             XREF_SUBM);
124   struct tag_struct ts;
125   Gedcom_ctxt self1, self2;
126
127   /* first generate "0 SUBM" */
128   ts.string = "SUBM";
129   ts.value  = TAG_SUBM;
130   self1 = start_record(REC_SUBM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
131                        NULL, GEDCOM_MAKE_NULL(val2));
132
133   /* then generate "1 NAME ..." */
134   ts.string = "NAME";
135   ts.value  = TAG_NAME;
136   self2 = start_element(ELT_SUBM_NAME, self1, 1, ts, DEFAULT_SUBMITTER_NAME,
137                         GEDCOM_MAKE_STRING(val1, DEFAULT_SUBMITTER_NAME));
138
139   /* close "1 NAME ..." */
140   end_element(ELT_SUBM_NAME, self1, self2, NULL);
141
142   /* close "0 SUBM" */
143   end_record(REC_SUBM, self1, NULL);
144 }
145
146 void compat_generate_gedcom(Gedcom_ctxt parent)
147 {
148   struct tag_struct ts;
149   Gedcom_ctxt self1, self2;
150   
151   /* first generate "1 GEDC" */
152   ts.string = "GEDC";
153   ts.value  = TAG_GEDC;
154   self1 = start_element(ELT_HEAD_GEDC, parent, 1, ts, NULL,
155                         GEDCOM_MAKE_NULL(val1));
156   
157   /* then generate "2 VERS <DEFAULT_GEDC_VERS>" */
158   ts.string = "VERS";
159   ts.value  = TAG_VERS;
160   self2 = start_element(ELT_HEAD_GEDC_VERS, self1, 2, ts,
161                         DEFAULT_GEDCOM_VERS,
162                         GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_VERS));
163   
164   /* close "2 VERS" */
165   end_element(ELT_HEAD_GEDC_VERS, self1, self2, NULL);
166   
167   /* then generate "2 FORM <DEFAULT_GEDCOM_FORM> */
168   ts.string = "FORM";
169   ts.value  = TAG_FORM;
170   self2 = start_element(ELT_HEAD_GEDC_FORM, self1, 2, ts,
171                         DEFAULT_GEDCOM_FORM,
172                         GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
173   
174   /* close "2 FORM" */
175   end_element(ELT_HEAD_GEDC_FORM, self1, self2, NULL);
176   
177   /* close "1 GEDC" */
178   end_element(ELT_HEAD_GEDC, parent, self1, NULL);
179 }
180
181 int compat_generate_char(Gedcom_ctxt parent)
182 {
183   struct tag_struct ts;
184   Gedcom_ctxt self1;
185   char* charset;
186   
187   /* first generate "1 CHAR <DEFAULT_CHAR>" */
188   ts.string = "CHAR";
189   ts.value  = TAG_CHAR;
190
191   /* Must strdup, because default_charset is const char */
192   charset   = strdup(default_charset);
193   if (! charset)
194     MEMORY_ERROR;
195   else {
196     self1 = start_element(ELT_HEAD_CHAR, parent, 1, ts, charset,
197                           GEDCOM_MAKE_STRING(val1, charset));
198     free(charset);
199     
200     /* close "1 CHAR" */
201     end_element(ELT_HEAD_CHAR, parent, self1, NULL);
202   }
203   if (open_conv_to_internal(default_charset) == 0)
204     return 1;
205   else
206     return 0;
207 }
208
209 Gedcom_ctxt compat_generate_resi_start(Gedcom_ctxt parent)
210 {
211   Gedcom_ctxt self;
212   struct tag_struct ts;
213
214   ts.string = "RESI";
215   ts.value  = TAG_RESI;
216   self = start_element(ELT_SUB_INDIV_RESI, parent, 1, ts, NULL,
217                        GEDCOM_MAKE_NULL(val1));
218   return self;
219 }
220
221 void compat_generate_resi_end(Gedcom_ctxt parent, Gedcom_ctxt self)
222 {
223   end_element(ELT_SUB_INDIV_RESI, parent, self, NULL);
224 }