Unlink xrefs properly when struct is deleted.
[gedcom-parse.git] / gom / user_rec.c
index 6581c8a820b190f3ea450c1bbfb0ee473bd16ef6..a685f3def47c8f4bdaa7d2da41d918429e16dac8 100644 (file)
@@ -57,12 +57,15 @@ Gedcom_ctxt user_rec_start(_REC_PARAMS_)
 {
   struct user_rec* user = NULL;
   struct xref_value* xr = NULL;
+  Gom_ctxt result  = NULL;
   Gedcom_ctxt ctxt = NULL;
+  int err = 0;
+  
   if (GEDCOM_IS_XREF_PTR(xref))
     xr = GEDCOM_XREF_PTR(xref);
   if (xr) {
     if (! xr->object) {
-      user = make_user_record(xr->string);
+      user = MAKEFUNC(user_rec)(xr->string);
       xr->object = (Gedcom_ctxt) user;
     }
     ctxt = xr->object;
@@ -71,90 +74,171 @@ Gedcom_ctxt user_rec_start(_REC_PARAMS_)
     }
   }
   else {
-    user = make_user_record(NULL);
+    user = MAKEFUNC(user_rec)(NULL);
     ctxt = (Gedcom_ctxt) user;
   }
+
+  if (user) {
+    user->tag = strdup(tag);
+    if (! user->tag) {
+      MEMORY_ERROR;
+      err = 1;
+    }
+    else if (GEDCOM_IS_STRING(parsed_value)) {
+      user->str_value = strdup(GEDCOM_STRING(parsed_value));
+      if (!user->str_value) {
+       MEMORY_ERROR;
+       err = 1;
+      }
+    }
+    else if (GEDCOM_IS_XREF_PTR(parsed_value))
+      user->xref_value = GEDCOM_XREF_PTR(parsed_value);
+    
+    if (! err)
+      result = MAKE_GOM_CTXT(rec, user_rec, ctxt);
+  }
   
-  user->tag = strdup(tag);
-  if (GEDCOM_IS_STRING(parsed_value))
-    user->str_value = strdup(GEDCOM_STRING(parsed_value));
-  else if (GEDCOM_IS_XREF_PTR(parsed_value))
-    user->xref_value = GEDCOM_XREF_PTR(parsed_value);
-  
-  return (Gedcom_ctxt) MAKE_GOM_CTXT(rec, user_rec, ctxt);
+  return (Gedcom_ctxt)result;
 }
 
-GET_REC_BY_XREF(user_rec, XREF_USER, gom_get_user_rec_by_xref)
+DEFINE_DESTROYFUNC(user_rec, gom_first_user_rec)
+DEFINE_DELETEFUNC(user_rec)
+DEFINE_GETXREFFUNC(user_rec, XREF_USER)
+
+DEFINE_ADDFUNC2(user_rec, user_data, extra)
+
+/* Specific function, because xrefstr not mandatory here */
+struct user_rec* MAKEFUNC(user_rec)(const char* xrefstr)
+{
+  struct user_rec* rec = NULL;
+  MAKE_CHAIN_ELT(user_rec, gom_first_user_rec, rec);
+  if (rec && xrefstr) {
+    rec->xrefstr = strdup(xrefstr);
+    if (! rec->xrefstr) MEMORY_ERROR;
+  }
+  return rec;
+}
+
+struct user_rec* ADDFUNC(user_rec)(const char* xrefstr, const char* tag)
+{
+  struct user_rec *obj = NULL;
+  struct xref_value* xrv = gedcom_get_by_xref(xrefstr);
+  if (tag && tag[0] == '_') {
+    if (xrv)
+      gom_xref_already_in_use(xrefstr);
+    else {
+      obj = MAKEFUNC(user_rec)(xrefstr);
+      if (obj) {
+       obj->tag = strdup(tag);
+       if (! obj->tag)
+         MEMORY_ERROR;
+       else
+         xrv = gedcom_add_xref(XREF_USER, xrefstr, (Gedcom_ctxt)obj);
+       if (!xrv) {
+         DESTROYFUNC(user_rec)(obj);
+         obj = NULL;
+       }
+      }
+    }
+  }
+  return obj;
+}
 
 Gedcom_ctxt user_elt_start(_ELT_PARAMS_)
 {
   Gom_ctxt ctxt = (Gom_ctxt)parent;
-  struct user_data *data = (struct user_data *)malloc(sizeof(struct user_data));
-  memset (data, 0, sizeof(struct user_data));
-
-  data->level = level;
-  data->tag = strdup(tag);
-  if (GEDCOM_IS_STRING(parsed_value))
-    data->str_value = strdup(GEDCOM_STRING(parsed_value));
-  else if (GEDCOM_IS_XREF_PTR(parsed_value))
-    data->xref_value = GEDCOM_XREF_PTR(parsed_value);
-
-  if (ctxt) {
-    switch (ctxt->obj_type) {
-      case T_header:
-       header_add_user_data(ctxt, data); break;
-      case T_submission:
-       submission_add_user_data(ctxt, data); break;
-      case T_submitter:
-       submitter_add_user_data(ctxt, data); break;
-      case T_family:
-       family_add_user_data(ctxt, data); break;
-      case T_individual:
-       individual_add_user_data(ctxt, data); break;
-      case T_multimedia:
-       multimedia_add_user_data(ctxt, data); break;
-      case T_note:
-       note_add_user_data(ctxt, data); break;
-      case T_repository:
-       repository_add_user_data(ctxt, data); break;
-      case T_source:
-       source_add_user_data(ctxt, data); break;
-      case T_user_rec:
-       user_rec_add_user_data(ctxt, data); break;
-      case T_address:
-       address_add_user_data(ctxt, data); break;
-      case T_event:
-       event_add_user_data(ctxt, data); break;
-      case T_place:
-       place_add_user_data(ctxt, data); break;
-      case T_source_citation:
-       citation_add_user_data(ctxt, data); break;
-      case T_note_sub:
-       note_sub_add_user_data(ctxt, data); break;
-      case T_multimedia_link:
-       multimedia_link_add_user_data(ctxt, data); break;
-      case T_lds_event:
-       lds_event_add_user_data(ctxt, data); break;
-      case T_user_ref_number:
-       user_ref_add_user_data(ctxt, data); break;
-      case T_change_date:
-       change_date_add_user_data(ctxt, data); break;
-      case T_personal_name:
-       name_add_user_data(ctxt, data); break;
-      case T_family_link:
-       family_link_add_user_data(ctxt, data); break;
-      case T_association:
-       association_add_user_data(ctxt, data); break;
-      case T_source_event:
-       source_event_add_user_data(ctxt, data); break;
-      case T_source_description:
-       source_description_add_user_data(ctxt, data); break;
-      default:
-       UNEXPECTED_CONTEXT(ctxt->ctxt_type);
+  Gom_ctxt result = NULL;
+  int err = 0;
+
+  if (! ctxt)
+    NO_CONTEXT;
+  else {
+    struct user_data *data
+      = (struct user_data *)malloc(sizeof(struct user_data));
+
+    if (! data)
+      MEMORY_ERROR;
+    else {
+      memset (data, 0, sizeof(struct user_data));
+      
+      data->level = level;
+      data->tag = strdup(tag);
+      if (! data->tag) {
+       MEMORY_ERROR;
+       free(data);
+       err = 1;
+      }
+      else if (GEDCOM_IS_STRING(parsed_value)) {
+       data->str_value = strdup(GEDCOM_STRING(parsed_value));
+       if (! data->str_value) {
+         MEMORY_ERROR;
+         free(data->tag);
+         free(data->str_value);
+         err = 1;
+       }
+      }
+      else if (GEDCOM_IS_XREF_PTR(parsed_value))
+       data->xref_value = GEDCOM_XREF_PTR(parsed_value);
+
+      if (! err) {
+       switch (ctxt->obj_type) {
+         case T_header:
+           ADDFUNC2(header,user_data)(ctxt, data); break;
+         case T_submission:
+           ADDFUNC2(submission,user_data)(ctxt, data); break;
+         case T_submitter:
+           ADDFUNC2(submitter,user_data)(ctxt, data); break;
+         case T_family:
+           ADDFUNC2(family,user_data)(ctxt, data); break;
+         case T_individual:
+           ADDFUNC2(individual,user_data)(ctxt, data); break;
+         case T_multimedia:
+           ADDFUNC2(multimedia,user_data)(ctxt, data); break;
+         case T_note:
+           ADDFUNC2(note,user_data)(ctxt, data); break;
+         case T_repository:
+           ADDFUNC2(repository,user_data)(ctxt, data); break;
+         case T_source:
+           ADDFUNC2(source,user_data)(ctxt, data); break;
+         case T_user_rec:
+           ADDFUNC2(user_rec,user_data)(ctxt, data); break;
+         case T_address:
+           ADDFUNC2(address,user_data)(ctxt, data); break;
+         case T_event:
+           ADDFUNC2(event,user_data)(ctxt, data); break;
+         case T_place:
+           ADDFUNC2(place,user_data)(ctxt, data); break;
+         case T_source_citation:
+           ADDFUNC2(source_citation,user_data)(ctxt, data); break;
+         case T_note_sub:
+           ADDFUNC2(note_sub,user_data)(ctxt, data); break;
+         case T_multimedia_link:
+           ADDFUNC2(multimedia_link,user_data)(ctxt, data); break;
+         case T_lds_event:
+           ADDFUNC2(lds_event,user_data)(ctxt, data); break;
+         case T_user_ref_number:
+           ADDFUNC2(user_ref_number,user_data)(ctxt, data); break;
+         case T_change_date:
+           ADDFUNC2(change_date,user_data)(ctxt, data); break;
+         case T_personal_name:
+           ADDFUNC2(personal_name,user_data)(ctxt, data); break;
+         case T_family_link:
+           ADDFUNC2(family_link,user_data)(ctxt, data); break;
+         case T_association:
+           ADDFUNC2(association,user_data)(ctxt, data); break;
+         case T_source_event:
+           ADDFUNC2(source_event,user_data)(ctxt, data); break;
+         case T_source_description:
+           ADDFUNC2(source_description,user_data)(ctxt, data); break;
+         default:
+           UNEXPECTED_CONTEXT(ctxt->ctxt_type);
+       }
+       result = make_gom_ctxt(elt, ctxt->obj_type, ctxt->ctxt_ptr);
+      }
     }
   }
   
-  return (Gedcom_ctxt) make_gom_ctxt(elt, ctxt->obj_type, ctxt->ctxt_ptr);
+  return (Gedcom_ctxt)result;
 }
 
 void user_rec_subscribe()
@@ -163,29 +247,44 @@ void user_rec_subscribe()
   gedcom_subscribe_to_element(ELT_USER, user_elt_start, def_elt_end);
 }
 
-void user_rec_add_user_data(Gom_ctxt ctxt, struct user_data* data)
+void UNREFALLFUNC(user_data)(struct user_data *obj)
+{
+  if (obj) {
+    struct user_data* runner;
+    for (runner = obj; runner; runner = runner->next)
+      unref_xref_value(runner->xref_value);
+  }
+}
+
+void CLEANFUNC(user_data)(struct user_data* data)
 {
-  struct user_rec *obj = SAFE_CTXT_CAST(user_rec, ctxt);
-  LINK_CHAIN_ELT(user_data, obj->extra, data)
+  if (data) {
+    SAFE_FREE(data->tag);
+    SAFE_FREE(data->str_value);
+  }
 }
 
-void user_data_cleanup(struct user_data* data)
+void UNREFALLFUNC(user_rec)(struct user_rec *obj)
 {
-  SAFE_FREE(data->tag);
-  SAFE_FREE(data->str_value);
+  if (obj) {
+    unref_xref_value(obj->xref_value);
+    UNREFALLFUNC(user_data)(obj->extra);
+  }
 }
 
-void user_rec_cleanup(struct user_rec* rec)
+void CLEANFUNC(user_rec)(struct user_rec* rec)
 {
-  SAFE_FREE(rec->xrefstr);
-  SAFE_FREE(rec->tag);
-  SAFE_FREE(rec->str_value);
-  DESTROY_CHAIN_ELTS(user_data, rec->extra, user_data_cleanup);
+  if (rec) {
+    SAFE_FREE(rec->xrefstr);
+    SAFE_FREE(rec->tag);
+    SAFE_FREE(rec->str_value);
+    DESTROY_CHAIN_ELTS(user_data, rec->extra);
+  }
 }
 
 void user_recs_cleanup()
 {
-  DESTROY_CHAIN_ELTS(user_rec, gom_first_user_rec, user_rec_cleanup);
+  DESTROY_CHAIN_ELTS(user_rec, gom_first_user_rec);
 }
 
 struct user_rec* gom_get_first_user_rec()
@@ -193,11 +292,38 @@ struct user_rec* gom_get_first_user_rec()
   return gom_first_user_rec;
 }
 
-struct user_rec* make_user_record(char* xrefstr)
+int write_user_recs(Gedcom_write_hndl hndl)
 {
-  struct user_rec* rec;
-  MAKE_CHAIN_ELT(user_rec, gom_first_user_rec, rec);
-  if (xrefstr)
-    rec->xrefstr = strdup(xrefstr);
-  return rec;
+  int result = 0;
+  struct user_rec* obj;
+
+  for (obj = gom_first_user_rec; obj; obj = obj->next) {
+    if (obj->xref_value)
+      result |= gedcom_write_user_xref(hndl, 0, obj->tag, obj->xrefstr,
+                                      obj->xref_value);
+    else
+      result |= gedcom_write_user_str(hndl, 0, obj->tag, obj->xrefstr,
+                                     obj->str_value);
+    if (obj->extra)
+      result |= write_user_data(hndl, obj->extra);
+  }
+  return result;  
+}
+
+int write_user_data(Gedcom_write_hndl hndl, struct user_data* data)
+{
+  int result = 0;
+  struct user_data* obj;
+
+  if (!data) return 1;
+
+  for (obj = data; data; data = data->next) {
+    if (obj->xref_value)
+      result |= gedcom_write_user_xref(hndl, obj->level, obj->tag, NULL,
+                                      obj->xref_value);
+    else
+      result |= gedcom_write_user_str(hndl, obj->level, obj->tag, NULL,
+                                     obj->str_value);
+  }
+  return result;
 }