Also write empty CONT lines.
[gedcom-parse.git] / gedcom / compat.c
index be9b3a1ae4210546740cde88295f83435b9b3a76..833bac1dede62236970cf8dcd20d0cc311177101 100644 (file)
@@ -30,7 +30,9 @@
 #include "gedcom.h"
 
 int compat_enabled = 1;
-int compatibility  = 0; 
+int compatibility  = 0;
+int compatibility_program = 0;
+int compatibility_version = 0;
 const char* default_charset = "";
 
 #define SUBMITTER_LINK         "@__COMPAT__SUBM__@"
@@ -39,10 +41,27 @@ const char* default_charset = "";
 #define DEFAULT_GEDCOM_VERS    "5.5"
 #define DEFAULT_GEDCOM_FORM    "LINEAGE-LINKED"
 
+enum _COMPAT_PROGRAM {
+  CP_FTREE = 1,
+  CP_LIFELINES,
+  CP_PAF,
+  CP_FAMORIG
+};
+
+const char* program_name[] = {
+  /* NULL */         "",
+  /* CP_FTREE */     "ftree",
+  /* CP_LIFELINES */ "Lifelines",
+  /* CP_PAF */       "Personal Ancestral File",
+  /* CP_FAMORIG */   "Family Origins"
+};
+
 enum _COMPAT {
   C_FTREE = 0x01,
   C_LIFELINES = 0x02,
-  C_PAF = 0x04
+  C_PAF5 = 0x04,
+  C_PAF2 = 0x08,
+  C_FAMORIG = 0x10
 };
 
 /* Incompatibility list (with GEDCOM 5.5):
@@ -60,24 +79,34 @@ enum _COMPAT {
        - '@' not written as '@@' in values
        - lots of missing required values
 
-    - Personal Ancestral File:
+    - Personal Ancestral File 5:
         - '@' not written as '@@' in values
        - some 5.5.1 (draft) tags are used: EMAIL, FONE, ROMN
        - no FAMC field in SLGC
+
+    - Personal Ancestral File 2:
+        - '@' not written as '@@' in values
+       - COMM tag in submitter record
+       - double dates written as e.g. '1815/1816' instead of '1815/16'
+
+    - Family Origins:
+        - '@' not written as '@@' in values
  */
 
 int compat_matrix[] =
 {
-  /* C_NO_SUBMITTER */        C_FTREE | C_LIFELINES,
+  /* C_NO_SUBMITTER */        C_FTREE | C_LIFELINES | C_PAF2,
   /* C_INDI_ADDR */           C_FTREE,
   /* C_NOTE_NO_VALUE */       C_FTREE,
-  /* C_NO_GEDC */             C_LIFELINES,
+  /* C_NO_GEDC */             C_LIFELINES | C_PAF2,
   /* C_NO_CHAR */             C_LIFELINES,
   /* C_HEAD_TIME */           C_LIFELINES,
-  /* C_NO_DOUBLE_AT */        C_LIFELINES | C_PAF,
+  /* C_NO_DOUBLE_AT */        C_LIFELINES | C_PAF5 | C_PAF2 | C_FAMORIG,
   /* C_NO_REQUIRED_VALUES */  C_LIFELINES,
-  /* C_551_TAGS */            C_PAF,
-  /* C_NO_SLGC_FAMC */        C_PAF
+  /* C_551_TAGS */            C_PAF5,
+  /* C_NO_SLGC_FAMC */        C_PAF5,
+  /* C_SUBM_COMM */           C_PAF2,
+  /* C_DOUBLE_DATES_4 */      C_PAF2
 };
 
 int compat_state[C_NR_OF_RULES];
@@ -89,34 +118,100 @@ void gedcom_set_compat_handling(int enable_compat)
   compat_enabled = enable_compat;
 }
 
-void enable_compat_msg(const char* program_name)
+void enable_compat_msg(const char* program_name, int version)
+{
+  if (version > 0)
+    gedcom_warning(_("Enabling compatibility with '%s', version %d"),
+                  program_name, version);
+  else
+    gedcom_warning(_("Enabling compatibility with '%s'"),
+                  program_name);
+}
+
+int program_equal(const char* program, const char* compare)
+{
+  return !strncmp(program, compare, strlen(compare)+1);
+}
+
+int program_equal_continued(const char* program, const char* compare)
+{
+  size_t len = strlen(compare);
+  int result = strncmp(program, compare, len);
+  if (result == 0) {
+    if (strlen(program) > len)
+      set_compatibility_version(program + len);
+  }
+  return !result;
+}
+
+void set_compatibility_program(const char* program)
 {
-  gedcom_warning(_("Enabling compatibility with '%s'"), program_name);
+  compatibility_program = 0;
+  if (compat_enabled) {
+    if (program_equal(program, "ftree")) {
+      compatibility_program = CP_FTREE;
+    }
+    else if (program_equal_continued(program, "LIFELINES")) {
+      compatibility_program = CP_LIFELINES;
+    }
+    else if (program_equal_continued(program, "PAF")) {
+      compatibility_program = CP_PAF;
+    }
+    else if (program_equal(program, "FamilyOrigins")) {
+      compatibility_program = CP_FAMORIG;
+    }
+  }
 }
 
-void set_compatibility(const char* program)
+void compute_compatibility()
 {
   /* Reinitialize compatibility */
   int i;
+  int version = 0;
   default_charset = "";
   compatibility = 0;
   for (i = 0; i < C_NR_OF_RULES; i++)
     compat_state[i] = 0;
-  
-  if (compat_enabled) {
-    if (! strncmp(program, "ftree", 6)) {
-      enable_compat_msg("ftree");
+
+  switch (compatibility_program) {
+    case CP_FTREE:
       compatibility = C_FTREE;
-    }
-    else if (! strncmp(program, "LIFELINES", 9)) {
-      /* Matches "LIFELINES 3.0.2" */
-      enable_compat_msg("Lifelines");
+      break;
+    case CP_LIFELINES:
       compatibility = C_LIFELINES;
       default_charset = "ANSI";
-    }
-    else if (! strncmp(program, "PAF", 4)) {
-      enable_compat_msg("Personal Ancestral File");
-      compatibility = C_PAF;
+      break;
+    case CP_PAF:
+      if (compatibility_version >= 20000 && compatibility_version < 30000) {
+       compatibility = C_PAF2;
+       version = 2;
+      }
+      else if (compatibility_version >= 50000) {
+       compatibility = C_PAF5;
+       version = 5;
+      }
+      break;
+    case CP_FAMORIG:
+      compatibility = C_FAMORIG;
+      break;
+    default:
+      break;
+  }
+  if (compatibility)
+    enable_compat_msg(program_name[compatibility_program], version);
+}
+
+void set_compatibility_version(const char* version)
+{
+  if (compat_enabled) {
+    unsigned int major=0, minor=0, patch=0;
+    int result;
+    
+    result = sscanf(version, " %u.%u.%u", &major, &minor, &patch);
+    if (result > 0) {
+      gedcom_debug_print("Setting compat version to %u.%u.%u",
+                        major, minor, patch);
+      compatibility_version = major * 10000 + minor * 100 + patch;
     }
   }
 }
@@ -126,6 +221,10 @@ int compat_mode(Compat_rule rule)
   return (compat_matrix[rule] & compatibility);
 }
 
+/********************************************************************/
+/*  C_NO_SUBMITTER                                                  */
+/********************************************************************/
+
 void compat_generate_submitter_link(Gedcom_ctxt parent)
 {
   struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_USED,
@@ -173,6 +272,10 @@ void compat_generate_submitter()
   }
 }
 
+/********************************************************************/
+/*  C_NO_GEDC                                                       */
+/********************************************************************/
+
 void compat_generate_gedcom(Gedcom_ctxt parent)
 {
   struct tag_struct ts;
@@ -208,6 +311,10 @@ void compat_generate_gedcom(Gedcom_ctxt parent)
   end_element(ELT_HEAD_GEDC, parent, self1, NULL);
 }
 
+/********************************************************************/
+/*  C_NO_CHAR                                                       */
+/********************************************************************/
+
 int compat_generate_char(Gedcom_ctxt parent)
 {
   struct tag_struct ts;
@@ -236,6 +343,10 @@ int compat_generate_char(Gedcom_ctxt parent)
     return 0;
 }
 
+/********************************************************************/
+/*  C_INDI_ADDR                                                     */
+/********************************************************************/
+
 Gedcom_ctxt compat_generate_resi_start(Gedcom_ctxt parent)
 {
   Gedcom_ctxt self;
@@ -253,6 +364,10 @@ void compat_generate_resi_end(Gedcom_ctxt parent, Gedcom_ctxt self)
   end_element(ELT_SUB_INDIV_RESI, parent, self, NULL);
 }
 
+/********************************************************************/
+/*  C_551_TAGS                                                      */
+/********************************************************************/
+
 int is_551_tag(const char* tag)
 {
   if (strncmp(tag, "EMAIL", 6))
@@ -279,6 +394,10 @@ int compat_check_551_tag(const char* tag, struct safe_buffer* b)
     return 0;
 }
 
+/********************************************************************/
+/*  C_NO_SLGC_FAMC                                                  */
+/********************************************************************/
+
 void compat_generate_slgc_famc_link(Gedcom_ctxt parent)
 {
   struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_USED,
@@ -316,3 +435,60 @@ void compat_generate_slgc_famc_fam()
     end_record(REC_FAM, self, NULL);
   }
 }
+
+/********************************************************************/
+/*  C_SUBM_COMM                                                     */
+/********************************************************************/
+
+int compat_check_subm_comm(const char* tag, const char* parent_tag,
+                          struct safe_buffer* b)
+{
+  if (!strcmp(tag, "COMM") && !strcmp(parent_tag, "SUBM")) {
+    reset_buffer(b);
+    SAFE_BUF_ADDCHAR(b, '_');
+    safe_buf_append(b, tag);
+    gedcom_warning(_("Converting non-standard tag '%s' to user tag '%s'"),
+                  tag, get_buf_string(b));
+    compat_state[C_SUBM_COMM] = 1;
+    return 1;
+  }
+  else
+    return 0;
+}
+
+void compat_close_subm_comm()
+{
+  compat_state[C_SUBM_COMM] = 0;
+}
+
+int compat_check_subm_comm_cont(const char* tag)
+{
+  if (compat_state[C_SUBM_COMM] && !strcmp(tag, "CONT")) {
+    compat_state[C_SUBM_COMM] = 2;
+    return 1;
+  }
+  else
+    return 0;
+}
+
+Gedcom_ctxt compat_subm_comm_cont_start(Gedcom_ctxt parent, char* str)
+{
+  Gedcom_ctxt self = NULL;
+  struct tag_struct ts;
+
+  if (compat_state[C_SUBM_COMM] == 2) {
+    ts.string = "_CONT";
+    ts.value  = USERTAG;
+    self = start_element(ELT_USER, parent, 2, ts, str, &val2);
+  }
+
+  return self;
+}
+
+void compat_subm_comm_cont_end(Gedcom_ctxt parent, Gedcom_ctxt self)
+{
+  if (compat_state[C_SUBM_COMM] == 2) {
+    end_element(ELT_USER, parent, self, NULL);
+    compat_state[C_SUBM_COMM] = 1;
+  }
+}