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