Don't lose the HEAD.TIME section of Lifelines.
[gedcom-parse.git] / gedcom / compat.c
index 833bac1dede62236970cf8dcd20d0cc311177101..15f6b06d1190a3f445650c0cac5e49e8c2e6332d 100644 (file)
@@ -41,27 +41,37 @@ const char* default_charset = "";
 #define DEFAULT_GEDCOM_VERS    "5.5"
 #define DEFAULT_GEDCOM_FORM    "LINEAGE-LINKED"
 
+struct program_data {
+  const char* name;
+  int         default_compat;
+  const char* default_charset;
+};
+
 enum _COMPAT_PROGRAM {
   CP_FTREE = 1,
   CP_LIFELINES,
   CP_PAF,
-  CP_FAMORIG
+  CP_FAMORIG,
+  CP_EASYTREE
 };
 
-const char* program_name[] = {
-  /* NULL */         "",
-  /* CP_FTREE */     "ftree",
-  /* CP_LIFELINES */ "Lifelines",
-  /* CP_PAF */       "Personal Ancestral File",
-  /* CP_FAMORIG */   "Family Origins"
+enum _COMPAT {
+  C_FTREE        = 0x0001,
+  C_LIFELINES    = 0x0002,
+  C_PAF5         = 0x0004,
+  C_PAF2         = 0x0008,
+  C_FAMORIG      = 0x0010,
+  C_EASYTREE     = 0x0020,
+  C_PAF4         = 0x0040
 };
 
-enum _COMPAT {
-  C_FTREE = 0x01,
-  C_LIFELINES = 0x02,
-  C_PAF5 = 0x04,
-  C_PAF2 = 0x08,
-  C_FAMORIG = 0x10
+struct program_data data[] = {
+  /* NULL */         { "", 0, "" },
+  /* CP_FTREE */     { "ftree", C_FTREE, "" },
+  /* CP_LIFELINES */ { "Lifelines", C_LIFELINES, "ANSI" },
+  /* CP_PAF */       { "Personal Ancestral File", C_PAF5, "" },
+  /* CP_FAMORIG */   { "Family Origins", C_FAMORIG, "" },
+  /* CP_EASYTREE */  { "EasyTree", C_EASYTREE, "" }
 };
 
 /* Incompatibility list (with GEDCOM 5.5):
@@ -83,6 +93,7 @@ enum _COMPAT {
         - '@' not written as '@@' in values
        - some 5.5.1 (draft) tags are used: EMAIL, FONE, ROMN
        - no FAMC field in SLGC
+       - uses tab character (will be converted to 8 spaces here)
 
     - Personal Ancestral File 2:
         - '@' not written as '@@' in values
@@ -91,25 +102,43 @@ enum _COMPAT {
 
     - Family Origins:
         - '@' not written as '@@' in values
+       - CONC needs an extra space
+
+    - EasyTree:
+        - no GEDC.FORM field
+       - no submitter link in the header
+       - NOTE doesn't have a value
+       - NOTE.NOTE instead of NOTE.COND
+
+    - Personal Ancestral File 4:
+        - '@' not written as '@@' in values
  */
 
 int compat_matrix[] =
 {
-  /* C_NO_SUBMITTER */        C_FTREE | C_LIFELINES | C_PAF2,
+  /* C_NO_SUBMITTER */        C_FTREE | C_LIFELINES | C_PAF2 | C_EASYTREE,
   /* C_INDI_ADDR */           C_FTREE,
-  /* C_NOTE_NO_VALUE */       C_FTREE,
+  /* C_NOTE_NO_VALUE */       C_FTREE | C_EASYTREE,
   /* C_NO_GEDC */             C_LIFELINES | C_PAF2,
   /* C_NO_CHAR */             C_LIFELINES,
   /* C_HEAD_TIME */           C_LIFELINES,
-  /* C_NO_DOUBLE_AT */        C_LIFELINES | C_PAF5 | C_PAF2 | C_FAMORIG,
-  /* C_NO_REQUIRED_VALUES */  C_LIFELINES,
+  /* C_NO_DOUBLE_AT */        C_LIFELINES | C_PAF5 | C_PAF2 | C_FAMORIG
+                               | C_PAF4,
+  /* C_NO_REQUIRED_VALUES */  C_LIFELINES | C_PAF5,
   /* C_551_TAGS */            C_PAF5,
   /* C_NO_SLGC_FAMC */        C_PAF5,
   /* C_SUBM_COMM */           C_PAF2,
-  /* C_DOUBLE_DATES_4 */      C_PAF2
+  /* C_DOUBLE_DATES_4 */      C_PAF2 | C_PAF5 | C_PAF4,
+  /* C_CONC_NEEDS_SPACE */    C_FAMORIG,
+  /* C_NO_GEDC_FORM */        C_EASYTREE,
+  /* C_NOTE_NOTE */           C_EASYTREE,
+  /* C_TAB_CHARACTER */       C_PAF5
 };
 
-int compat_state[C_NR_OF_RULES];
+union _COMPAT_STATE {
+  int i;
+  void* vp;
+} compat_state[C_NR_OF_RULES];
 
 /* Compatibility handling */
 
@@ -160,6 +189,9 @@ void set_compatibility_program(const char* program)
     else if (program_equal(program, "FamilyOrigins")) {
       compatibility_program = CP_FAMORIG;
     }
+    else if (program_equal(program, "EasyTree")) {
+      compatibility_program = CP_EASYTREE;
+    }
   }
 }
 
@@ -171,34 +203,31 @@ void compute_compatibility()
   default_charset = "";
   compatibility = 0;
   for (i = 0; i < C_NR_OF_RULES; i++)
-    compat_state[i] = 0;
+    compat_state[i].i = 0;
 
   switch (compatibility_program) {
-    case CP_FTREE:
-      compatibility = C_FTREE;
-      break;
-    case CP_LIFELINES:
-      compatibility = C_LIFELINES;
-      default_charset = "ANSI";
-      break;
     case CP_PAF:
       if (compatibility_version >= 20000 && compatibility_version < 30000) {
        compatibility = C_PAF2;
        version = 2;
       }
+      if (compatibility_version >= 40000 && compatibility_version < 50000) {
+       compatibility = C_PAF4;
+       version = 4;
+      }
       else if (compatibility_version >= 50000) {
        compatibility = C_PAF5;
        version = 5;
       }
       break;
-    case CP_FAMORIG:
-      compatibility = C_FAMORIG;
-      break;
     default:
+      compatibility = data[compatibility_program].default_compat;
       break;
   }
-  if (compatibility)
-    enable_compat_msg(program_name[compatibility_program], version);
+  if (compatibility) {
+    default_charset = data[compatibility_program].default_charset;
+    enable_compat_msg(data[compatibility_program].name, version);
+  }
 }
 
 void set_compatibility_version(const char* version)
@@ -240,12 +269,12 @@ void compat_generate_submitter_link(Gedcom_ctxt parent)
                       parent, 1, ts, SUBMITTER_LINK,
                       GEDCOM_MAKE_XREF_PTR(val1, xr));
   end_element(ELT_HEAD_SUBM, parent, self, NULL);
-  compat_state[C_NO_SUBMITTER] = 1;
+  compat_state[C_NO_SUBMITTER].i = 1;
 }
 
 void compat_generate_submitter()
 {
-  if (compat_state[C_NO_SUBMITTER]) {
+  if (compat_state[C_NO_SUBMITTER].i) {
     struct xref_value *xr = gedcom_parse_xref(SUBMITTER_LINK, XREF_DEFINED,
                                              XREF_SUBM);
     struct tag_struct ts;
@@ -268,7 +297,7 @@ void compat_generate_submitter()
     
     /* close "0 SUBM" */
     end_record(REC_SUBM, self1, NULL);
-    compat_state[C_NO_SUBMITTER] = 0;
+    compat_state[C_NO_SUBMITTER].i = 0;
   }
 }
 
@@ -298,19 +327,33 @@ void compat_generate_gedcom(Gedcom_ctxt parent)
   end_element(ELT_HEAD_GEDC_VERS, self1, self2, NULL);
   
   /* then generate "2 FORM <DEFAULT_GEDCOM_FORM> */
+  compat_generate_gedcom_form(self1);
+  
+  /* close "1 GEDC" */
+  end_element(ELT_HEAD_GEDC, parent, self1, NULL);
+}
+
+/********************************************************************/
+/*  C_NO_GEDC_FORM                                                  */
+/********************************************************************/
+
+void compat_generate_gedcom_form(Gedcom_ctxt parent)
+{
+  struct tag_struct ts;
+  Gedcom_ctxt self;
+  
+  /* generate "2 FORM <DEFAULT_GEDCOM_FORM> */
   ts.string = "FORM";
   ts.value  = TAG_FORM;
-  self2 = start_element(ELT_HEAD_GEDC_FORM, self1, 2, ts,
-                       DEFAULT_GEDCOM_FORM,
-                       GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
+  self = start_element(ELT_HEAD_GEDC_FORM, parent, 2, ts,
+                      DEFAULT_GEDCOM_FORM,
+                      GEDCOM_MAKE_STRING(val1, DEFAULT_GEDCOM_FORM));
   
   /* close "2 FORM" */
-  end_element(ELT_HEAD_GEDC_FORM, self1, self2, NULL);
+  end_element(ELT_HEAD_GEDC_FORM, parent, self, NULL);
   
-  /* close "1 GEDC" */
-  end_element(ELT_HEAD_GEDC, parent, self1, NULL);
 }
-
+  
 /********************************************************************/
 /*  C_NO_CHAR                                                       */
 /********************************************************************/
@@ -343,6 +386,37 @@ int compat_generate_char(Gedcom_ctxt parent)
     return 0;
 }
 
+/********************************************************************/
+/*  C_HEAD_TIME                                                     */
+/********************************************************************/
+
+void compat_save_head_date_context(Gedcom_ctxt parent)
+{
+  compat_state[C_HEAD_TIME].vp = parent;
+}
+
+Gedcom_ctxt compat_generate_head_time_start(int level, struct tag_struct ts,
+                                           char* value)
+{
+  Gedcom_ctxt parent = compat_state[C_HEAD_TIME].vp;
+  if (!value)
+    value = "-";
+  if (parent)
+    return start_element(ELT_HEAD_DATE_TIME,
+                        parent, level, ts, value,
+                        GEDCOM_MAKE_STRING(val1, value));
+  else
+    return NULL;
+}
+
+void compat_generate_head_time_end(Gedcom_ctxt self)
+{
+  Gedcom_ctxt parent = compat_state[C_HEAD_TIME].vp;
+  if (parent)
+    end_element(ELT_HEAD_DATE_TIME,
+               parent, self, GEDCOM_MAKE_NULL(val1));
+}
+
 /********************************************************************/
 /*  C_INDI_ADDR                                                     */
 /********************************************************************/
@@ -413,13 +487,13 @@ void compat_generate_slgc_famc_link(Gedcom_ctxt parent)
                       parent, 2, ts, SLGC_FAMC_LINK,
                       GEDCOM_MAKE_XREF_PTR(val1, xr));
   end_element(ELT_SUB_LIO_SLGC_FAMC, parent, self, NULL);
-  compat_state[C_NO_SLGC_FAMC]++;
+  compat_state[C_NO_SLGC_FAMC].i++;
 }
 
 void compat_generate_slgc_famc_fam()
 {
   /* If bigger than 1, then the FAM record has already been generated */
-  if (compat_state[C_NO_SLGC_FAMC] == 1) {
+  if (compat_state[C_NO_SLGC_FAMC].i == 1) {
     struct xref_value *xr = gedcom_parse_xref(SLGC_FAMC_LINK, XREF_DEFINED,
                                              XREF_FAM);
     struct tag_struct ts;
@@ -449,7 +523,7 @@ int compat_check_subm_comm(const char* tag, const char* parent_tag,
     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;
+    compat_state[C_SUBM_COMM].i = 1;
     return 1;
   }
   else
@@ -458,13 +532,13 @@ int compat_check_subm_comm(const char* tag, const char* parent_tag,
 
 void compat_close_subm_comm()
 {
-  compat_state[C_SUBM_COMM] = 0;
+  compat_state[C_SUBM_COMM].i = 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;
+  if (compat_state[C_SUBM_COMM].i && !strcmp(tag, "CONT")) {
+    compat_state[C_SUBM_COMM].i = 2;
     return 1;
   }
   else
@@ -476,7 +550,7 @@ 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) {
+  if (compat_state[C_SUBM_COMM].i == 2) {
     ts.string = "_CONT";
     ts.value  = USERTAG;
     self = start_element(ELT_USER, parent, 2, ts, str, &val2);
@@ -487,8 +561,8 @@ Gedcom_ctxt compat_subm_comm_cont_start(Gedcom_ctxt parent, char* str)
 
 void compat_subm_comm_cont_end(Gedcom_ctxt parent, Gedcom_ctxt self)
 {
-  if (compat_state[C_SUBM_COMM] == 2) {
+  if (compat_state[C_SUBM_COMM].i == 2) {
     end_element(ELT_USER, parent, self, NULL);
-    compat_state[C_SUBM_COMM] = 1;
+    compat_state[C_SUBM_COMM].i = 1;
   }
 }