Copied from old documentation. Removed all Gedcom_val details.
[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 Gedcom_compat compat_options = 0;
34 int compatibility  = 0;
35 int compatibility_program = 0;
36 int compatibility_version = 0;
37 const char* default_charset = "";
38
39 void cleanup_compat_buffer();
40 struct safe_buffer compat_buffer = { NULL, 0, NULL, 0, cleanup_compat_buffer };
41
42 void cleanup_compat_buffer()
43 {
44   cleanup_buffer(&compat_buffer);
45 }
46
47 #define SUBMITTER_LINK         "@__COMPAT__SUBM__@"
48 #define SLGC_FAMC_LINK         "@__COMPAT__FAM_SLGC__@"
49 #define DEFAULT_SUBMITTER_NAME "Submitter"
50 #define DEFAULT_GEDCOM_VERS    "5.5"
51 #define DEFAULT_GEDCOM_FORM    "LINEAGE-LINKED"
52
53 struct program_data {
54   const char* name;
55   int         default_compat;
56   const char* default_charset;
57 };
58
59 enum _COMPAT_PROGRAM {
60   CP_FTREE = 1,
61   CP_LIFELINES,
62   CP_PAF,
63   CP_FAMORIG,
64   CP_EASYTREE
65 };
66
67 enum _COMPAT {
68   C_FTREE        = 0x0001,
69   C_LIFELINES    = 0x0002,
70   C_PAF5         = 0x0004,
71   C_PAF2         = 0x0008,
72   C_FAMORIG      = 0x0010,
73   C_EASYTREE     = 0x0020,
74   C_PAF4         = 0x0040
75 };
76
77 struct program_data data[] = {
78   /* NULL */         { "", 0, "" },
79   /* CP_FTREE */     { "ftree", C_FTREE, "" },
80   /* CP_LIFELINES */ { "Lifelines", C_LIFELINES, "ANSI" },
81   /* CP_PAF */       { "Personal Ancestral File", C_PAF5, "" },
82   /* CP_FAMORIG */   { "Family Origins", C_FAMORIG, "" },
83   /* CP_EASYTREE */  { "EasyTree", C_EASYTREE, "" }
84 };
85
86 /* Incompatibility list (with GEDCOM 5.5):
87
88     - ftree:
89         - no submitter record, no submitter link in the header
90         - INDI.ADDR instead of INDI.RESI.ADDR
91         - NOTE doesn't have a value
92
93     - Lifelines (3.0.2):
94         - no submitter record, no submitter link in the header
95         - no GEDC field in the header
96         - no CHAR field in the header
97         - HEAD.TIME instead of HEAD.DATE.TIME (will be ignored here)
98         - '@' not written as '@@' in values
99         - lots of missing required values
100
101     - Personal Ancestral File 5:
102         - '@' not written as '@@' in values
103         - some 5.5.1 (draft) tags are used: EMAIL, FONE, ROMN
104         - no FAMC field in SLGC
105         - uses tab character (will be converted to 8 spaces here)
106         - lines too long
107         - double dates written as e.g. '1815/1816'
108
109     - Personal Ancestral File 2:
110         - '@' not written as '@@' in values
111         - COMM tag in submitter record
112         - double dates written as e.g. '1815/1816'
113
114     - Family Origins:
115         - '@' not written as '@@' in values
116         - CONC needs an extra space
117
118     - EasyTree:
119         - no GEDC.FORM field
120         - no submitter link in the header
121         - NOTE doesn't have a value
122         - NOTE.NOTE instead of NOTE.COND
123         - NOTE.CONC.SOUR instead of NOTE.SOUR
124         - non-standard tags in SOUR records
125
126     - Personal Ancestral File 4:
127         - '@' not written as '@@' in values
128         - SUBM.CTRY instead of SUBM.ADDR.CTRY
129         - lines too long
130         - double dates written as e.g. '1815/1816'
131  */
132
133 int compat_matrix[] =
134 {
135   /* C_NO_SUBMITTER */        C_FTREE | C_LIFELINES | C_PAF2 | C_EASYTREE,
136   /* C_INDI_ADDR */           C_FTREE,
137   /* C_NOTE_NO_VALUE */       C_FTREE | C_EASYTREE,
138   /* C_NO_GEDC */             C_LIFELINES | C_PAF2,
139   /* C_NO_CHAR */             C_LIFELINES,
140   /* C_HEAD_TIME */           C_LIFELINES,
141   /* C_NO_DOUBLE_AT */        C_LIFELINES | C_PAF5 | C_PAF2 | C_FAMORIG
142                                | C_PAF4,
143   /* C_NO_REQUIRED_VALUES */  C_LIFELINES | C_PAF5 | C_EASYTREE,
144   /* C_551_TAGS */            C_PAF5,
145   /* C_NO_SLGC_FAMC */        C_PAF5,
146   /* C_SUBM_COMM */           C_PAF2,
147   /* C_DOUBLE_DATES_4 */      C_PAF2 | C_PAF5 | C_PAF4,
148   /* C_CONC_NEEDS_SPACE */    C_FAMORIG,
149   /* C_NO_GEDC_FORM */        C_EASYTREE,
150   /* C_NOTE_NOTE */           C_EASYTREE,
151   /* C_TAB_CHARACTER */       C_PAF5,
152   /* C_SUBM_CTRY */           C_PAF4,
153   /* C_NOTE_TOO_LONG */       C_PAF4 | C_PAF5,
154   /* C_NOTE_CONC_SOUR */      C_EASYTREE,
155   /* C_NONSTD_SOUR_TAGS */    C_EASYTREE,
156 };
157
158 union _COMPAT_STATE {
159   int i;
160   void* vp;
161 } compat_state[C_NR_OF_RULES];
162
163 /* Compatibility handling */
164
165 void gedcom_set_compat_handling(int enable_compat)
166 {
167   compat_enabled = enable_compat;
168 }
169
170 void gedcom_set_compat_options(Gedcom_compat options)
171 {
172   compat_options = options;
173 }
174
175 void enable_compat_msg(const char* program_name, int version)
176 {
177   if (version > 0)
178     gedcom_warning(_("Enabling compatibility with '%s', version %d"),
179                    program_name, version);
180   else
181     gedcom_warning(_("Enabling compatibility with '%s'"),
182                    program_name);
183 }
184
185 int program_equal(const char* program, const char* compare)
186 {
187   return !strncmp(program, compare, strlen(compare)+1);
188 }
189
190 int program_equal_continued(const char* program, const char* compare)
191 {
192   size_t len = strlen(compare);
193   int result = strncmp(program, compare, len);
194   if (result == 0) {
195     if (strlen(program) > len)
196       set_compatibility_version(program + len);
197   }
198   return !result;
199 }
200
201 void set_compatibility_program(const char* program)
202 {
203   compatibility_program = 0;
204   if (compat_enabled) {
205     if (program_equal(program, "ftree")) {
206       compatibility_program = CP_FTREE;
207     }
208     else if (program_equal_continued(program, "LIFELINES")) {
209       compatibility_program = CP_LIFELINES;
210     }
211     else if (program_equal_continued(program, "PAF")) {
212       compatibility_program = CP_PAF;
213     }
214     else if (program_equal(program, "FamilyOrigins")) {
215       compatibility_program = CP_FAMORIG;
216     }
217     else if (program_equal(program, "EasyTree")) {
218       compatibility_program = CP_EASYTREE;
219     }
220   }
221 }
222
223 void compute_compatibility()
224 {
225   /* Reinitialize compatibility */
226   int i;
227   int version = 0;
228   default_charset = "";
229   compatibility = 0;
230   for (i = 0; i < C_NR_OF_RULES; i++)
231     compat_state[i].i = 0;
232
233   switch (compatibility_program) {
234     case CP_PAF:
235       if (compatibility_version >= 20000 && compatibility_version < 30000) {
236         compatibility = C_PAF2;
237         version = 2;
238       }
239       if (compatibility_version >= 40000 && compatibility_version < 50000) {
240         compatibility = C_PAF4;
241         version = 4;
242       }
243       else if (compatibility_version >= 50000) {
244         compatibility = C_PAF5;
245         version = 5;
246       }
247       break;
248     default:
249       compatibility = data[compatibility_program].default_compat;
250       break;
251   }
252   if (compatibility) {
253     default_charset = data[compatibility_program].default_charset;
254     enable_compat_msg(data[compatibility_program].name, version);
255   }
256 }
257
258 void set_compatibility_version(const char* version)
259 {
260   if (compat_enabled) {
261     unsigned int major=0, minor=0, patch=0;
262     int result;
263     
264     result = sscanf(version, " %u.%u.%u", &major, &minor, &patch);
265     if (result > 0) {
266       gedcom_debug_print("Setting compat version to %u.%u.%u",
267                          major, minor, patch);
268       compatibility_version = major * 10000 + minor * 100 + patch;
269     }
270   }
271 }
272
273 int compat_mode(Compat_rule rule)
274 {
275   return (compat_matrix[rule] & compatibility);
276 }
277
278 void compat_close()
279 {
280   compatibility_program = 0;
281   compatibility = 0;
282 }
283
284 /********************************************************************/
285 /*  C_NO_SUBMITTER                                                  */
286 /********************************************************************/
287
288 void compat_generate_submitter_link(Gedcom_ctxt parent)
289 {
290   struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_USED,
291                                             XREF_SUBM);
292   struct tag_struct ts;
293   Gedcom_ctxt self;
294   
295   ts.string = "SUBM";
296   ts.value  = TAG_SUBM;
297   gedcom_warning(_("Adding link to submitter record with xref '%s'"),
298                  SUBMITTER_LINK);
299   self = start_element(ELT_HEAD_SUBM,
300                        parent, 1, ts, SUBMITTER_LINK,
301                        GEDCOM_MAKE_XREF_PTR(val1, xr));
302   end_element(ELT_HEAD_SUBM, parent, self, NULL);
303   compat_state[C_NO_SUBMITTER].i = 1;
304 }
305
306 void compat_generate_submitter()
307 {
308   if (compat_state[C_NO_SUBMITTER].i) {
309     struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_DEFINED,
310                                               XREF_SUBM);
311     struct tag_struct ts;
312     Gedcom_ctxt self1, self2;
313     
314     /* first generate "0 SUBM" */
315     ts.string = "SUBM";
316     ts.value  = TAG_SUBM;
317     self1 = start_record(REC_SUBM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
318                          NULL, GEDCOM_MAKE_NULL(val2));
319     
320     /* then generate "1 NAME ..." */
321     ts.string = "NAME";
322     ts.value  = TAG_NAME;
323     self2 = start_element(ELT_SUBM_NAME, self1, 1, ts, DEFAULT_SUBMITTER_NAME,
324                           GEDCOM_MAKE_STRING(val1, DEFAULT_SUBMITTER_NAME));
325     
326     /* close "1 NAME ..." */
327     end_element(ELT_SUBM_NAME, self1, self2, NULL);
328     
329     /* close "0 SUBM" */
330     end_record(REC_SUBM, self1, NULL);
331     compat_state[C_NO_SUBMITTER].i = 0;
332   }
333 }
334
335 /********************************************************************/
336 /*  C_NO_GEDC                                                       */
337 /********************************************************************/
338
339 void compat_generate_gedcom(Gedcom_ctxt parent)
340 {
341   struct tag_struct ts;
342   Gedcom_ctxt self1, self2;
343   
344   /* first generate "1 GEDC" */
345   ts.string = "GEDC";
346   ts.value  = TAG_GEDC;
347   self1 = start_element(ELT_HEAD_GEDC, parent, 1, ts, NULL,
348                         GEDCOM_MAKE_NULL(val1));
349   
350   /* then generate "2 VERS <DEFAULT_GEDC_VERS>" */
351   ts.string = "VERS";
352   ts.value  = TAG_VERS;
353   self2 = start_element(ELT_HEAD_GEDC_VERS, self1, 2, ts,
354                         DEFAULT_GEDCOM_VERS,
355                         GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_VERS));
356   
357   /* close "2 VERS" */
358   end_element(ELT_HEAD_GEDC_VERS, self1, self2, NULL);
359   
360   /* then generate "2 FORM <DEFAULT_GEDCOM_FORM> */
361   compat_generate_gedcom_form(self1);
362   
363   /* close "1 GEDC" */
364   end_element(ELT_HEAD_GEDC, parent, self1, NULL);
365 }
366
367 /********************************************************************/
368 /*  C_NO_GEDC_FORM                                                  */
369 /********************************************************************/
370
371 void compat_generate_gedcom_form(Gedcom_ctxt parent)
372 {
373   struct tag_struct ts;
374   Gedcom_ctxt self;
375   
376   /* generate "2 FORM <DEFAULT_GEDCOM_FORM> */
377   ts.string = "FORM";
378   ts.value  = TAG_FORM;
379   self = start_element(ELT_HEAD_GEDC_FORM, parent, 2, ts,
380                        DEFAULT_GEDCOM_FORM,
381                        GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
382   
383   /* close "2 FORM" */
384   end_element(ELT_HEAD_GEDC_FORM, parent, self, NULL);
385   
386 }
387   
388 /********************************************************************/
389 /*  C_NO_CHAR                                                       */
390 /********************************************************************/
391
392 int compat_generate_char(Gedcom_ctxt parent)
393 {
394   struct tag_struct ts;
395   Gedcom_ctxt self1;
396   char* charset;
397   
398   /* first generate "1 CHAR <DEFAULT_CHAR>" */
399   ts.string = "CHAR";
400   ts.value  = TAG_CHAR;
401
402   /* Must strdup, because default_charset is const char */
403   charset   = strdup(default_charset);
404   if (! charset)
405     MEMORY_ERROR;
406   else {
407     self1 = start_element(ELT_HEAD_CHAR, parent, 1, ts, charset,
408                           GEDCOM_MAKE_STRING(val1, charset));
409     free(charset);
410     
411     /* close "1 CHAR" */
412     end_element(ELT_HEAD_CHAR, parent, self1, NULL);
413   }
414   if (open_conv_to_internal(default_charset) == 0)
415     return 1;
416   else
417     return 0;
418 }
419
420 /********************************************************************/
421 /*  C_HEAD_TIME                                                     */
422 /********************************************************************/
423
424 void compat_save_head_date_context(Gedcom_ctxt parent)
425 {
426   compat_state[C_HEAD_TIME].vp = parent;
427 }
428
429 Gedcom_ctxt compat_generate_head_time_start(int level, struct tag_struct ts,
430                                             char* value)
431 {
432   if (compat_options & COMPAT_ALLOW_OUT_OF_CONTEXT) {
433     Gedcom_ctxt parent = compat_state[C_HEAD_TIME].vp;
434     if (!value)
435       value = "-";
436     if (parent)
437       return start_element(ELT_HEAD_DATE_TIME,
438                            parent, level, ts, value,
439                            GEDCOM_MAKE_STRING(val1, value));
440     else
441       return NULL;
442   }
443   else {
444     gedcom_warning(_("Header change time '%s' lost in the compatibility (out of context)"),
445                    value);
446     return NULL;
447   }
448 }
449
450 void compat_generate_head_time_end(Gedcom_ctxt self)
451 {
452   if (compat_options & COMPAT_ALLOW_OUT_OF_CONTEXT) {
453     Gedcom_ctxt parent = compat_state[C_HEAD_TIME].vp;
454     if (parent)
455       end_element(ELT_HEAD_DATE_TIME,
456                   parent, self, GEDCOM_MAKE_NULL(val1));
457   }
458 }
459
460 /********************************************************************/
461 /*  C_SUBM_CTRY                                                     */
462 /********************************************************************/
463
464 void compat_save_ctry_parent_context(Gedcom_ctxt parent)
465 {
466   compat_state[C_SUBM_CTRY].vp = parent;
467 }
468
469 Gedcom_ctxt compat_generate_addr_ctry_start(int level, struct tag_struct ts,
470                                             char* value)
471 {
472   if (compat_options & COMPAT_ALLOW_OUT_OF_CONTEXT) {
473     Gedcom_ctxt parent = compat_state[C_SUBM_CTRY].vp;
474     if (!value)
475       value = "-";
476     if (parent)
477       return start_element(ELT_SUB_ADDR_CTRY,
478                            parent, level, ts, value,
479                            GEDCOM_MAKE_STRING(val1, value));
480     else
481       return NULL;
482   }
483   else {
484     gedcom_warning(_("Country '%s' lost in the compatibility (out of context)"), value);
485     return NULL;
486   }
487 }
488
489 void compat_generate_addr_ctry_end(Gedcom_ctxt self)
490 {
491   if (compat_options & COMPAT_ALLOW_OUT_OF_CONTEXT) {
492     Gedcom_ctxt parent = compat_state[C_SUBM_CTRY].vp;
493     if (parent)
494       end_element(ELT_SUB_ADDR_CTRY,
495                   parent, self, GEDCOM_MAKE_NULL(val1));
496   }
497 }
498
499 void compat_free_ctry_parent_context()
500 {
501   compat_state[C_SUBM_CTRY].vp = NULL;
502 }
503
504 /********************************************************************/
505 /*  C_INDI_ADDR                                                     */
506 /********************************************************************/
507
508 Gedcom_ctxt compat_generate_resi_start(Gedcom_ctxt parent)
509 {
510   Gedcom_ctxt self;
511   struct tag_struct ts;
512
513   ts.string = "RESI";
514   ts.value  = TAG_RESI;
515   self = start_element(ELT_SUB_INDIV_RESI, parent, 1, ts, NULL,
516                        GEDCOM_MAKE_NULL(val1));
517   return self;
518 }
519
520 void compat_generate_resi_end(Gedcom_ctxt parent, Gedcom_ctxt self)
521 {
522   end_element(ELT_SUB_INDIV_RESI, parent, self, NULL);
523 }
524
525 /********************************************************************/
526 /*  C_551_TAGS                                                      */
527 /********************************************************************/
528
529 int is_551_tag(const char* tag)
530 {
531   if (strncmp(tag, "EMAIL", 6))
532     return 1;
533   else if (strncmp(tag, "FONE", 5))
534     return 1;
535   else if (strncmp(tag, "ROMN", 5))
536     return 1;
537   else
538     return 0;
539 }
540
541 int compat_check_551_tag(const char* tag, struct safe_buffer* b)
542 {
543   if (is_551_tag(tag)) {
544     reset_buffer(b);
545     SAFE_BUF_ADDCHAR(b, '_');
546     safe_buf_append(b, tag);
547     gedcom_warning(_("Converting 5.5.1 tag '%s' to standard 5.5 user tag '%s'"),
548                    tag, get_buf_string(b));
549     return 1;
550   }
551   else
552     return 0;
553 }
554
555 /********************************************************************/
556 /*  C_NO_SLGC_FAMC                                                  */
557 /********************************************************************/
558
559 void compat_generate_slgc_famc_link(Gedcom_ctxt parent)
560 {
561   struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_USED,
562                                             XREF_FAM);
563   struct tag_struct ts;
564   Gedcom_ctxt self;
565   
566   ts.string = "FAMC";
567   ts.value  = TAG_FAMC;
568   gedcom_warning(_("Adding link to family record with xref '%s'"),
569                  SLGC_FAMC_LINK);
570   self = start_element(ELT_SUB_LIO_SLGC_FAMC,
571                        parent, 2, ts, SLGC_FAMC_LINK,
572                        GEDCOM_MAKE_XREF_PTR(val1, xr));
573   end_element(ELT_SUB_LIO_SLGC_FAMC, parent, self, NULL);
574   compat_state[C_NO_SLGC_FAMC].i++;
575 }
576
577 void compat_generate_slgc_famc_fam()
578 {
579   /* If bigger than 1, then the FAM record has already been generated */
580   if (compat_state[C_NO_SLGC_FAMC].i == 1) {
581     struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_DEFINED,
582                                               XREF_FAM);
583     struct tag_struct ts;
584     Gedcom_ctxt self;
585     
586     /* generate "0 FAM" */
587     ts.string = "FAM";
588     ts.value  = TAG_FAM;
589     self = start_record(REC_FAM, 0, GEDCOM_MAKE_XREF_PTR(val1, xr), ts,
590                         NULL, GEDCOM_MAKE_NULL(val2));
591     
592     /* close "0 FAM" */
593     end_record(REC_FAM, self, NULL);
594   }
595 }
596
597 /********************************************************************/
598 /*  C_SUBM_COMM                                                     */
599 /********************************************************************/
600
601 int compat_check_subm_comm(const char* tag, const char* parent_tag,
602                            struct safe_buffer* b)
603 {
604   if (!strcmp(tag, "COMM") && !strcmp(parent_tag, "SUBM")) {
605     reset_buffer(b);
606     SAFE_BUF_ADDCHAR(b, '_');
607     safe_buf_append(b, tag);
608     gedcom_warning(_("Converting non-standard tag '%s' to user tag '%s'"),
609                    tag, get_buf_string(b));
610     compat_state[C_SUBM_COMM].i = 1;
611     return 1;
612   }
613   else
614     return 0;
615 }
616
617 void compat_close_subm_comm()
618 {
619   compat_state[C_SUBM_COMM].i = 0;
620 }
621
622 int compat_check_subm_comm_cont(const char* tag)
623 {
624   if (compat_state[C_SUBM_COMM].i && !strcmp(tag, "CONT")) {
625     compat_state[C_SUBM_COMM].i = 2;
626     return 1;
627   }
628   else
629     return 0;
630 }
631
632 Gedcom_ctxt compat_subm_comm_cont_start(Gedcom_ctxt parent, char* str)
633 {
634   Gedcom_ctxt self = NULL;
635   struct tag_struct ts;
636
637   if (compat_state[C_SUBM_COMM].i == 2) {
638     ts.string = "_CONT";
639     ts.value  = USERTAG;
640     self = start_element(ELT_USER, parent, 2, ts, str, &val2);
641   }
642
643   return self;
644 }
645
646 void compat_subm_comm_cont_end(Gedcom_ctxt parent, Gedcom_ctxt self)
647 {
648   if (compat_state[C_SUBM_COMM].i == 2) {
649     end_element(ELT_USER, parent, self, NULL);
650     compat_state[C_SUBM_COMM].i = 1;
651   }
652 }
653
654 /********************************************************************/
655 /*  C_DOUBLE_DATES_4                                                */
656 /********************************************************************/
657
658 void compat_date_start()
659 {
660   if (compat_mode(C_DOUBLE_DATES_4)) {
661     reset_buffer(&compat_buffer);
662     compat_state[C_DOUBLE_DATES_4].i = 0;
663   }
664 }
665
666 int compat_double_date_check(char* year2)
667 {
668   return (compat_mode(C_DOUBLE_DATES_4)
669           && !compat_state[C_DOUBLE_DATES_4].i
670           && strlen(year2) == 4);
671 }
672
673 int compat_double_date_final(struct date_value* dv, const char** curr_line)
674 {
675   char* compat_line_value = get_buf_string(&compat_buffer);
676   compat_state[C_DOUBLE_DATES_4].i = 1;
677   if (compat_line_value && compat_line_value[0]
678       && (dv->type == DV_NO_MODIFIER || dv->type == DV_ABOUT)
679       && dv->date1.day == -1
680       && dv->date1.month == -1) {
681     gedcom_warning(_("Converting '%s' to standard '%s'"),
682                    *curr_line, compat_line_value);
683     *curr_line = compat_line_value;
684   }
685   return 1;
686 }
687
688 int compat_date_check(struct date_value* dv, const char** curr_line)
689 {
690   if (compat_mode(C_DOUBLE_DATES_4)
691       && compat_double_date_final(dv, curr_line)) {
692     return 1;
693   }
694   else {
695     return 0;
696   }
697 }
698
699 /********************************************************************/
700 /*  C_NOTE_TOO_LONG                                                 */
701 /********************************************************************/
702
703 char compat_prefix[MAXGEDCLINELEN];
704
705 int compat_long_line(int level, int tag)
706 {
707   return compat_mode(C_NOTE_TOO_LONG) && (level > 0) && (tag == TAG_NOTE);
708 }
709
710 char* compat_long_line_get_prefix(char* str)
711 {
712   if (str && utf8_strlen(str) > MAXGEDCLINELEN - 7) {
713     int len = MAXGEDCLINELEN - 7;
714     char* ch     = nth_utf8_char(str, len - 1);
715     char* nextch = next_utf8_char(ch);
716     memset(compat_prefix, 0, MAXGEDCLINELEN);
717     while (len > 1 && (*ch == ' ' || *nextch == ' ')) {
718       len--;
719       nextch = ch;
720       ch     = nth_utf8_char(str, len - 1);
721     }
722     len = nextch - str;
723     strncpy(compat_prefix, str, len);
724     compat_state[C_NOTE_TOO_LONG].vp = (void*)nextch;
725     return compat_prefix;
726   }
727   else {
728     compat_state[C_NOTE_TOO_LONG].vp = NULL;
729     return str;
730   }
731 }
732
733 void compat_long_line_finish(Gedcom_ctxt parent, int level)
734 {
735   struct tag_struct ts;
736   ts.string = "CONC";
737   ts.value  = TAG_CONC;
738   
739   while (compat_state[C_NOTE_TOO_LONG].vp) {
740     Gedcom_ctxt ctxt;
741     char* input  = (char*)compat_state[C_NOTE_TOO_LONG].vp;
742     char* output = compat_long_line_get_prefix(input);
743
744     ctxt = start_element(ELT_SUB_CONC, parent, level + 1, ts, output,
745                          GEDCOM_MAKE_STRING(val1, output));
746     end_element(ELT_SUB_CONC, parent, ctxt, GEDCOM_MAKE_NULL(val1));
747   }
748 }
749
750 /********************************************************************/
751 /*  C_NOTE_CONC_SOUR                                                */
752 /********************************************************************/
753
754 Gedcom_ctxt compat_generate_note_sour_start(Gedcom_ctxt parent,
755                                             int level, struct tag_struct ts,
756                                             char* pointer)
757 {
758   Gedcom_ctxt self;
759   struct xref_value *xr = gedcom_parse_xref(pointer, XREF_USED, XREF_SOUR);
760   if (xr == NULL) {
761     self = (void*)-1;
762   }
763   else {
764     self = start_element(ELT_SUB_SOUR, parent, level-1, ts, pointer,
765                          GEDCOM_MAKE_XREF_PTR(val1, xr));
766   }
767   compat_state[C_NOTE_CONC_SOUR].vp = parent;
768   return self;
769 }
770
771 void compat_generate_note_sour_end(Gedcom_ctxt self)
772 {
773   if (self != (void*) -1) {
774     end_element(ELT_SUB_SOUR, compat_state[C_NOTE_CONC_SOUR].vp,
775                 self, GEDCOM_MAKE_NULL(val1));
776   }
777 }
778
779 /********************************************************************/
780 /*  C_NONSTD_SOUR_TAGS                                              */
781 /********************************************************************/
782
783 int is_nonstd_sour_tag(const char* tag)
784 {
785   if (strncmp(tag, "FILN", 5))
786     return 1;
787   else if (strncmp(tag, "URL", 4))
788     return 1;
789   else if (strncmp(tag, "LOCA", 5))
790     return 1;
791   else if (strncmp(tag, "REGI", 5))
792     return 1;
793   else if (strncmp(tag, "VOL", 4))
794     return 1;
795   else
796     return 0;
797 }
798
799 int compat_check_sour_tag(const char* tag, struct safe_buffer* b)
800 {
801   if (is_nonstd_sour_tag(tag)) {
802     reset_buffer(b);
803     SAFE_BUF_ADDCHAR(b, '_');
804     safe_buf_append(b, tag);
805     gedcom_warning(_("Converting undefined tag '%s' to user tag '%s'"),
806                    tag, get_buf_string(b));
807     return 1;
808   }
809   else
810     return 0;
811 }
812
813 Gedcom_ctxt compat_generate_nonstd_sour_start(Gedcom_ctxt parent, int level,
814                                               struct tag_struct ts,
815                                               char* value,
816                                               struct safe_buffer* b)
817 {
818   Gedcom_ctxt self = NULL;
819   reset_buffer(b);
820   SAFE_BUF_ADDCHAR(b, '_');
821   safe_buf_append(b, ts.string);
822   gedcom_warning(_("Converting invalidly used tag '%s' to user tag '%s'"),
823                  ts.string, get_buf_string(b));
824   ts.string = get_buf_string(b);
825
826   self = start_element(ELT_USER, parent, level, ts, value,
827                        GEDCOM_MAKE_NULL_OR_STRING(val1, value));
828   compat_state[C_NONSTD_SOUR_TAGS].i = 1;
829   return self;
830 }
831
832 void compat_generate_nonstd_sour_end(Gedcom_ctxt parent, Gedcom_ctxt self)
833 {
834   end_element(ELT_USER, parent, self, NULL);
835   compat_state[C_NONSTD_SOUR_TAGS].i = 0;
836 }
837
838 int compat_generate_nonstd_sour_state()
839 {
840   return compat_state[C_NONSTD_SOUR_TAGS].i;
841 }